【问题标题】:Cannot insert into column "Id" error while trying to insert entity that has a nested parent entity尝试插入具有嵌套父实体的实体时无法插入列“Id”错误
【发布时间】:2020-11-14 19:31:27
【问题描述】:

我的 Mikro-Orm (v3.6.15) 模型(连接到 Postgresql - pg v8.3.0)中有 2 个具有一对多关系的实体:

Uygulama(父母)

@Entity({ tableName: "Uygulamalar", collection: "Uygulamalar" })
export class Uygulama {
  @PrimaryKey({ fieldName: "Id" })
  id!: number;
  @Property({ fieldName: "Adi" })
  adi!: string;
  @Property({ fieldName: "Kod" })
  kod!: string;
  @Property({ fieldName: "UygulamaSahibi" })
  uygulamaSahibi!: string;
  @Property({ fieldName: "createdAt" })
  createdAt = new Date();
  @Property({ fieldName: "updatedAt", onUpdate: () => new Date() })
  updatedAt = new Date();
  @OneToMany({ entity: () => Modul, mappedBy: "rootUygulama", cascade: [] })
  moduller = new Collection<Modul>(this);
}

模块(子)

export class Modul {
  @PrimaryKey({ fieldName: "Id" })
  id!: number;
  @Property({ fieldName: "Adi" })
  adi!: string;
  @Property({ fieldName: "Kod" })
  kod!: string;
  @Property({ fieldName: "createdAt" })
  createdAt = new Date();
  @Property({ fieldName: "updatedAt", onUpdate: () => new Date() })
  updatedAt = new Date();
  @ManyToOne({ entity: () => Uygulama, joinColumn: "UygulamaId", cascade: [],})
  rootUygulama!: Uygulama;
  @OneToMany({ entity: () => Ekran, mappedBy: "rootModul", orphanRemoval: true,})
  ekranlar = new Collection<Ekran>(this);
}

我有一个休息端点(Expressjs)从发布的 Http 请求正文创建 Modul 对象:

router.post("/", async (req, res) => {
  const modul = DI.modulRepository.create(req.body);
  await DI.modulRepository.persistAndFlush(modul);
  res.send(modul);
});

当我尝试在下面发布 JSON 对象以创建新的 Modul(rootUygulama 对象已在数据库中)时:

{
    "adi": "Deneme Modülü 3",
    "kod": "DM1",
    "rootUygulama": {
        "id": 66,
        "adi": "Deneme Uygulaması",
        "kod": "DU",
        "uygulamaSahibi": "xxxxxx",
        "createdAt": "2020-07-24T21:18:47.874Z",
        "updatedAt": "2020-07-24T21:18:47.874Z",
        "moduller": [
        ]
    }
}

我得到错误:

[query] insert into "Uygulamalar" ("Adi", "Id", "Kod", "UygulamaSahibi", "createdAt", "updatedAt") values ('Deneme Uygulaması', 66, 'DU', 'szengin', '2020-07-25 00:18:47.874', '2020-07-25 00:18:47.874') returning "Id" [took 6 ms]
node_modules/mikro-orm/dist/utils/Logger.js:22
[query] rollback
node_modules/mikro-orm/dist/utils/Logger.js:22
(node:14344) UnhandledPromiseRejectionWarning: error: insert into "Uygulamalar" ("Adi", "Id", "Kod", "UygulamaSahibi", "createdAt", "updatedAt") values ($1, $2, $3, $4, $5, $6) returning "Id" - cannot insert into column "Id"
    at Parser.parseErrorMessage (d:\NODEJS\BildirimYonetimi\backend\node_modules\pg-protocol\dist\parser.js:278:15)
    at Parser.handlePacket (d:\NODEJS\BildirimYonetimi\backend\node_modules\pg-protocol\dist\parser.js:126:29)
    at Parser.parse (d:\NODEJS\BildirimYonetimi\backend\node_modules\pg-protocol\dist\parser.js:39:38)
    at Socket.<anonymous> (d:\NODEJS\BildirimYonetimi\backend\node_modules\pg-protocol\dist\index.js:8:42)
    at Socket.emit (events.js:311:20)
    at Socket.EventEmitter.emit (domain.js:482:12)
    at addChunk (_stream_readable.js:294:12)
    at readableAddChunk (_stream_readable.js:275:11)
    at Socket.Readable.push (_stream_readable.js:209:10)
    at TCP.onStreamRead (internal/stream_base_commons.js:186:23)
<node_internals>/internal/process/warning.js:32
(node:14344) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
<node_internals>/internal/process/warning.js:32
(node:14344) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

当我发送如下 JSON 对象并成功插入数据库时​​

{
    "adi": "Deneme Modülü 3",
    "kod": "DM1",
    "rootUygulama": 66
}

即使我将空级联数组设置为关系属性存储库尝试插入父对象并失败。

我是否在配置中遗漏了什么?

编辑:

对于我的 Typescript 客户端,我将如何定义 rootUygulama 属性?

export interface Modul {
  id: number;
  adi: string;
  kod: string;
  createdAt: Date;
  updatedAt: Date;
  rootUygulama: Uygulama;
  ekranlar: Array<Ekran>;
}

应该是这样的

rootUygulama: Uygulama | number;

【问题讨论】:

    标签: mikro-orm


    【解决方案1】:

    这不是关于级联,行为是正确的。当你只传递 PK 时,它被认为是现有实体,如果你传递一个对象,它被认为是新实体。这与是否设置 PK 无关,如果您想要这种行为,您需要自己编程。

    MikroORM 基于变更集跟踪工作,因此只有托管对象(从数据库加载的实体)才能产生更新查询。如果您想为rootUygulama 对象触发更新查询,请使用em.nativeUpdate()。如果您仍想将有效负载作为对象发送,但您只关心 PK,您还可以将该实体显式合并到 EM(这样它就成为托管的,就像您从 db 加载它一样)。

    const modul = DI.modulRepository.create(req.body);
    
    // if we see PK, we merge, so it won't be considered as new object
    if (modul.rootUygulama.id) {
      DI.em.merge(modul.rootUygulama);
      // here we could also fire the `em.nativeUpdate(Uygulama, modul.rootUygulama);` 
      // to fire an update query, but then you should use `em.transactional()` to have that update query inside the same TX as the flush
    }
    
    await DI.modulRepository.persistAndFlush(modul);
    res.send(modul);
    

    顺便说一句,我强烈建议不要禁用级联,除非您真正了解自己在做什么,因为默认设置是级联合并和持久化,这是您通常想要/需要的。

    【讨论】:

    • 非常感谢您的回答。还有一个问题。我应该如何在客户端(Typescript)上创建 rootUygulama 属性?因为当我将 rootUygulama 定义为 Uygulama 的类型时,我将无法为其设置数字(Uygulama 对象的 FK)。
    • 视情况而定,有时我将其定义为两者的结合。一段时间以来,我一直在考虑添加一个前端助手,以允许在前端拥有相同类型的身份映射,以统一体验,但这目前只是一个尝试:]
    • 有道理。感谢您的快速回复:)
    猜你喜欢
    • 2020-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多