如果存在则如何更新实体或如果不存在则创建实体

Meg*_*oks 2 typeorm

这是我的实体。

// 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;
Run Code Online (Sandbox Code Playgroud)
// 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;
Run Code Online (Sandbox Code Playgroud)

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

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

当我第一次将我的令牌保存到数据库时,所有这些都被保存了。

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

ER_DUP_ENTRY:密钥“REL_d417e5d35f2434afc4bd48cb4d”的重复条目“36”

我阅读了有关 save 方法的文档。但为什么我得到错误,我无法理解。我希望记录会被更新。为什么我的令牌详细信息没有更新?

我了解如何使用 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'
Run Code Online (Sandbox Code Playgroud)

经过一些实验,我注意到当我指定令牌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);
}
Run Code Online (Sandbox Code Playgroud)

但是如果我没有指明令牌的 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);
}
Run Code Online (Sandbox Code Playgroud)

ER_DUP_ENTRY:密钥“REL_d417e5d35f2434afc4bd48cb4d”的重复条目“36”

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

1) TypeORM upsert - 如果不存在则创建

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

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

在此处输入图片说明

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

小智 7

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

根据docs,该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();
Run Code Online (Sandbox Code Playgroud)

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

这可以通过修改您的实体,删除@PrimaryGeneratedId和使 userId a 来完成@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;

Run Code Online (Sandbox Code Playgroud)

希望有帮助:)