首页 > 解决方案 > 如果存在则更新实体,如果不存在则创建实体

问题描述

这是我的实体。

// 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'

经过一些实验,我注意到当我指定令牌 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 - 如果不存在则创建

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

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

在此处输入图像描述

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

标签: typeorm

解决方案


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

根据文档,该save()函数具有以下行为:

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

但是,如果没有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;

希望有帮助:)


推荐阅读