TypeORM upsert - 如果不存在则创建

问题描述 投票:0回答:12

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;
}

类似:

let contraption = await thingRepository.upsert({ name : "Contraption" });
typescript typeorm
12个回答
68
投票

正如 Tomer Amir 所指出的,目前有一个针对 real 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();

旧答案实际上指向执行OP要求的“更新”方式:

已经有一个方法了:

Repository<T>.save()
,其中文档说:

将所有给定实体保存在数据库中。如果实体不存在于 然后数据库插入,否则更新。

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

所以使用 typeORM 更新插入是:

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

41
投票

对于 2021 年发现此问题的任何人,如果 Typeorm 的

Repository.save()
方法找到与主键匹配的内容,它将更新或插入。这也适用于 sqlite。

来自文档:

 /**
 * Saves all given entities in the database.
 * If entities do not exist in the database then inserts, otherwise updates.
 */

27
投票

2023 更新

现在你可以使用

upsert
方法,

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

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

首先使用

Unique
装饰器定义该约束

@Entity()
@Unique('constraint_name', ['col_one', 'col_two'])

然后在代码中就可以使用

upsert
方法了。

await this.yourRepository.upsert({name: 'John'}, ['constraint_name'])

25
投票

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

await connection.createQueryBuilder()
        .insert()
        .into(Post)
        .values(post)
        .orIgnore()
        .execute();

17
投票

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

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

12
投票

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

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();

9
投票

这对我有用。

我结合了@DedaDev的答案。

您的实体

@Entity()
@Unique('constraint_name', ['id'])

您的服务

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
  },
);


3
投票

现在有一个库可以插入 TypeORM 来帮助做到这一点。


2
投票

您可能想从 Repository 类中查看“预加载”方法:https://typeorm.delightful.studio/classes/repository_repository.repository.html#preload

从给定的计划 javascript 对象创建一个新实体。如果实体已存在于数据库中,则会加载该实体(以及与其相关的所有内容),将所有值替换为给定对象中的新值,然后返回该新实体。这个新实体实际上是从数据库实体加载的,所有属性都从新对象替换。请注意,给定的类实体对象必须具有实体 id/主键来查找实体。如果未找到具有给定 ID 的实体,则返回未定义。

如上所述,限制是需要通过ID来搜索。


0
投票

在 2024 年,使用 NestJS 和 TypeORM,您可以使用 upsert 并处理所有边缘情况。

假设您有一个具有名称和颜色的实体 Thing

@Entity("thing")
@Unique(["name", "color"])
export class Thing {
  @PrimaryGeneratedColumn()
  id: string;

  @Column()
  name: string;

  @Column()
  color: string;

  // list goes on
}

事情的

id
在整个数据库的其他表中使用,导致臭名昭著

错误:表“事物”上的更新或删除违反了外键约束...

您仍然可以使用

upsert
以及详细选项

await repository.upsert(
  [
    { name: "a red thing", color: "red" },
    { name: "a blue thing", color: "blue" },
  ],
  {
    conflictPaths: ["name", "color"],
    skipUpdateIfNoValuesChanged: true,
    upsertType: "on-conflict-do-update",
  }
);

如果还是不行,那你就有防弹了


const repository = dataSource.getRepository(Thing);

await repository
    .createQueryBuilder()
    .insert()
    .into(Thing)
    .values([
    { name: "a red thing", color: "red" },
    { name: "a blue thing", color: "blue" },
    ])
    .orUpdate(["name", "color"])
    .orIgnore()
    .execute();


-1
投票

在此处以用户身份记录您的实体

@OneToMany(type => Post, post => post.user, {
        cascade: true
    })
    posts: Post[];


export const saveAllPosts = async (req: Request, res: Response) => {
    const userRepository = getManager().getRepository(User);
    const postRepository = getManager().getRepository(Post);
    let i;
    let newUsers:any = [];
    let  newUser:any = {};
    let  newPost:any = {};
    for(i=1; i<=6; i ++) {
        newUser = await userRepository.findOne({ 
            where: { id: i} 
        });
        if(typeof newUser == "undefined") {
            newUser = new User();
            console.log("insert");
        } else  {
            console.log("update");
        }           
        newUser.name  = "naval pankaj test"+i;   

        newPost = await postRepository.findOne({ 
            where: { userId: i} 
        });
        if(typeof newPost == "undefined") {
            newPost = new Post();
            console.log("post insert");
        } else  {
            console.log("post update");
        }
        newPost.title = "naval pankaj add post title "+i;
        newUser.posts = [newPost];
        newUsers.push(newUser);     
    }
    await userRepository.save(newUsers);  
    res.send("complete");
};
© www.soinside.com 2019 - 2024. All rights reserved.