TypeORM upsert - 如果不存在则创建

Bey*_*Sea 7 typescript typeorm

TypeORM是否包含一些功能以避免这种情况:

let contraption = await thingRepository.findOne({ name : "Contraption" });

if(!contraption) // Create if not exist
{
    let newThing = new Thing();
    newThing.name = "Contraption"
    await thingRepository.save(newThing);
    contraption = newThing;
}
Run Code Online (Sandbox Code Playgroud)

就像是 :

let contraption = await thingRepository.upsert({ name : "Contraption" });
Run Code Online (Sandbox Code Playgroud)

Ded*_*Dev 27

2023 更新

现在你可以使用upsert方法,

await this.yourRepository.upsert({name: 'John'}, ['id']) // assuming id is unique
Run Code Online (Sandbox Code Playgroud)

如果您有由两列或更多列组成的约束,请执行以下操作:

Unique首先用装饰器定义约束

@Entity()
@Unique('constraint_name', ['col_one', 'col_two'])
Run Code Online (Sandbox Code Playgroud)

然后在代码中就可以使用upsert方法了。

await this.yourRepository.upsert({name: 'John'}, ['constraint_name'])
Run Code Online (Sandbox Code Playgroud)


Bey*_*Sea 17

正如Tomer Amir所指出的,目前有一个部分真正upsert解决方案,一个功能请求是在TypeORM的存储库上打开ATM:

TypeORM upsert功能请求

部分解决方案:

await connection.createQueryBuilder()
        .insert()
        .into(Post)
        .values(post2)
        .onConflict(`("id") DO NOTHING`)
        .execute();

await connection.createQueryBuilder()
        .insert()
        .into(Post)
        .values(post2)
        .onConflict(`("id") DO UPDATE SET "title" = :title`)
        .setParameter("title", post2.title)
        .execute();
Run Code Online (Sandbox Code Playgroud)

老答案实际上指的是做出OP要求的"更新"方式:

已经有了一种方法:Repository<T>.save()其文档说:

保存数据库中的所有给定实体.如果数据库中不存在实体,则插入,否则更新.

但是,如果未指定id或唯一字段集,则save方法无法知道您正在引用现有数据库对象.

因此,使用typeORM进行插入是:

let contraption = await thingRepository.save({id: 1, name : "New Contraption Name !"});
Run Code Online (Sandbox Code Playgroud)

  • 当`values` 传递一个记录数组而不是单个记录时,如何使用`onConflict`? (3认同)
  • 如何指定自动生成的 uuid 字段,以便“.save”可以工作并且如果对象已经存在则不插入? (2认同)

Don*_*ong 16

对于在 2021 年发现此问题的任何人,Typeorm 的Repository.save()方法将在找到与主键匹配的情况下进行更新或插入。这也适用于 sqlite。

从文档:

 /**
 * Saves all given entities in the database.
 * If entities do not exist in the database then inserts, otherwise updates.
 */
Run Code Online (Sandbox Code Playgroud)

  • 是否可以做非主键?就像我有“电子邮件”一样,这是独一无二的。我可以执行 `.save({ email: 'foo', Age: 29 })` 以便它应该更新包含 `email: foo` 的行吗? (18认同)
  • 对于每个想要使用非主键执行此操作的人,以下是操作方法:https://www.kindacode.com/snippet/typeorm-upsert-update-if-exists-create-if-not-exists/ (2认同)

小智 10

对于任何正在寻找插入多条记录的方法并正在使用 Postgres 和 TypeORM 的人,您都可以通过 exclude 关键字访问您尝试更新/插入的行。

const posts = [{ id: 1, title: "First Post" }, { id: 2, title: "Second Post" }];

await connection.createQueryBuilder()
        .insert()
        .into(Post)
        .values(posts)
        .onConflict(`("id") DO UPDATE SET "title" = excluded."title"`)
        .execute();
Run Code Online (Sandbox Code Playgroud)


m.m*_*sni 10

这对我有用。

我结合了@DedaDev的答案。

您的实体

@Entity()
@Unique('constraint_name', ['id'])
Run Code Online (Sandbox Code Playgroud)

您的服务

await this.yourRepository.upsert(
  {
    id: uuid,
    key1: value1,
    ...
  },
  {
    skipUpdateIfNoValuesChanged: true, // If true, postgres will skip the update if no values would be changed (reduces writes)
    conflictPaths: ['id'], // column(s) name that you would like to ON CONFLICT
  },
);
Run Code Online (Sandbox Code Playgroud)


Zch*_*ary 7

ONCONFLICT不适用于 MySQL时,这可能会有所帮助。来自Github

await getConnection()
  .createQueryBuilder()
  .insert()
  .into(GroupEntity)
  .values(updatedGroups)
  .orUpdate({ conflict_target: ['id'], overwrite: ['name', 'parentId', 'web', 'avatar', 'description'] })
  .execute();
Run Code Online (Sandbox Code Playgroud)


Mos*_*tov 5

使用INSERT IGNORE忽略 MySQL 和 Postgres 上的重复项:

await connection.createQueryBuilder()
        .insert()
        .into(Post)
        .values(post)
        .orIgnore()
        .execute();
Run Code Online (Sandbox Code Playgroud)

  • 它不仅适用于 MySQL,Postgres 也适用。 (2认同)
  • 这应该是公认的答案,因为“onConflict”被标记为已弃用 (2认同)