【问题标题】:How update entity if exist or create if not exist entity如果存在则更新实体,如果不存在则创建实体
【发布时间】:2019-11-28 10:53:25
【问题描述】:

这是我的实体。

// user

@PrimaryGeneratedColumn()
public id: number;

@Column({ type: 'varchar', nullable: false })
public email: string;

@Column({ type: 'varchar', nullable: false })
public password: string;

@OneToOne(() => Token, (token: Token) => token.user)
public token: Token;
// token

@PrimaryGeneratedColumn()
public id: number;

@Column({ type: 'varchar', nullable: false })
public uuid: string;

@Column({ type: 'integer', nullable: false })
public userId: number;

@OneToOne(() => User, (user: User) => user.hash, { cascade: ['insert', 'remove'] })
@JoinColumn({ name: 'userId' })
public user: User;

这就是我在数据库中保存当前数据的方式。

private async savePayload(tokenDto: CreateTokenDto) {
    const token = this.tokenRepository.create(tokenDto);
    return await this.tokenRepository.save(token);
}

当我第一次将我的令牌保存到数据库时,它全部都保存了。

当我第二次保存时,出现错误。

ER_DUP_ENTRY:键“REL_d417e5d35f2434afc4bd48cb4d”的重复条目“36”

我阅读了有关保存方法的文档。但是为什么我得到错误,我不明白。我希望记录会被更新。为什么我的令牌详细信息没有更新?

我了解如何使用 sql 来做到这一点。

INSERT INTO "Tokens" (UUID, USERID)
    VALUES ('d93ab036-768c-420a-98d6-2f80c79e6ae7', 36)
        ON CONFLICT (USERID) 
            DO UPDATE SET UUID = 'd93ab036-768c-420a-98d6-2f80c79e6ae7', 
                USERID = 36'

经过一些实验,我注意到当我指定token id时,保存或更新是成功的。

private async savePayload(tokenDto: CreateTokenDto) {
    const a = {
        id: 15,
        uuid: '343443443444444444444444',
        userId: 36,
    };
    const token = this.tokenRepository.create(a);
    return await this.tokenRepository.save(token);
}

但是如果我没有指明令牌的 id,我会得到一个错误。

private async savePayload(tokenDto: CreateTokenDto) {
    const a = {
        // id: 15,
        uuid: '343443443444444444444444',
        userId: 36,
    };
    const token = this.tokenRepository.create(a);
    return await this.tokenRepository.save(token);
}

ER_DUP_ENTRY:键“REL_d417e5d35f2434afc4bd48cb4d”的重复条目“36”

我搜索并找到了一些示例。

1) TypeORM upsert - create if not exist

2) https://github.com/typeorm/typeorm/issues/3342

他们说值必须是主键或唯一值。但是我的 userId 字段是一个索引,也是唯一的。

可以有哪些选项,为什么我的令牌没有更新?

【问题讨论】:

    标签: typeorm


    【解决方案1】:

    整个问题是Repository<T>.save() 函数行为的结果。

    根据the docssave() 函数具有以下行为:

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

    但是,如果实体中没有 id 字段(没有 PrimaryKey),save() 方法会假定该实体在数据库中不存在,并继续创建一个新实体,而不是更新现有实体。这就是当您在实体中定义 id 字段时它起作用的原因。

    考虑到这一点,save() 方法似乎不适用于您的情况。您需要使用 TypeORM 的查询构建器编写自定义查询。此自定义查询将非常接近您在问题中使用原始 SQL 编写的查询。

    你可以这样写(免责声明:我根本没有测试过代码!):

    
    const values = {
        uuid: '343443443444444444444444',
        userId: 36
    }
    
    await connection.createQueryBuilder()
                .insert()
                .into(Tokens)
                .values(post2)
                .onConflict(`("userId") DO UPDATE SET UUID = :uuid`)
                .setParameter("title", values.uuid)
                .execute();
    

    也许,您的另一个选择是将userId 字段设置为您的表的主键。这将解决 save() 函数的 upsert 问题。正如您所描述的, userId 字段是一个索引,它也是唯一的。因此,您可以轻松地将其设为主要字段。

    这可以通过修改您的实体、删除 @PrimaryGeneratedId 并将 userId 设置为 @PrimaryColumn 来完成:

    @Column({ type: 'varchar', nullable: false })
    public uuid: string;
    
    @PrimaryColumn()
    public userId: number;
    
    @OneToOne(() => User, (user: User) => user.hash, { cascade: ['insert', 'remove'] })
    @JoinColumn({ name: 'userId' })
    public user: User;
    
    

    希望有帮助:)

    【讨论】:

    • 是的,第二个选项按我的预期工作。这是最完整的答案。谢谢
    【解决方案2】:

    这并不是说表中已经有重复的条目,而是说那里已经有一个条目具有该主键的值,因此拒绝插入第二个条目。

    您会发现一个匹配的行,并且您的代码在错误点尝试插入第二个。

    在插入时处理重复:

    如果您尝试为主键(或唯一索引)插入重复值,您将始终收到该错误。有几种解决方法:在插入之前进行检查,或者进行更新(如果可能发生了变化)或者什么也不做。

    【讨论】:

      【解决方案3】:

      您可以使用 .save 进行更新和插入,或者检查是否存在然后 .update 否则 .save 例子

      async CreateNewRole(data: any): Promise<Role | any> {
              try {
                const entity = await this.roleRepository.create(data);
                const role = await this.roleRepository.save(entity);
                this.trackingService.create(data.user);
                return {
                  success: true,
                  role,
                };
              } catch (e) {
                //  code == 23505 means duplication key
                if (parseInt(e.code) === 23505) {
                  console.log('error : ', e.detail);
                  return {
                    success: false,
                    message: ROLE_ERROR_MESSAGES.ROLE_IS_FOUND,
                  };
                } else {
                  return {
                    success: false,
                  };
                }
              }
            }
          
            async UpdateRole(data: any, id: number): Promise<Role | any> {
              try {
                await this.roleRepository.update(id, { ...data.payload });
                this.trackingService.create(data.user);
                // todo this need to be refactored !!
                // return back the updated entity
                const role = await this.roleRepository.find({ id });
                console.log('role updated ', role);
                return {
                  role,
                  success: true,
                };
              } catch (e) {
                if (parseInt(e.code) === 23505) {
                  console.log('error : ', e.detail);
                  return {
                    success: false,
                    message: ROLE_ERROR_MESSAGES.ROLE_IS_FOUND,
                  };
                } else {
                  return {
                    success: false,
                  };
                }
              }
            }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-13
        • 2013-05-26
        • 1970-01-01
        • 2019-02-16
        • 2017-08-30
        • 2010-10-16
        相关资源
        最近更新 更多