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;
// 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”
我搜索并找到了一些示例。
2) https://github.com/typeorm/typeorm/issues/3342
他们说该值必须是主键或唯一值。但我的 userId 字段是一个索引,也是唯一的。
可以有哪些选项,为什么我的令牌没有更新?
解决方案
整个问题是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;
希望有帮助:)
推荐阅读
- swift - SwiftUI 箭头快捷方式在 iPad 上不起作用
- c++ - 如果用户键入无效值,为什么从 cin 读取的这段代码会无限递归?
- java - 选择下一个值 firebase android
- html - Jquery 不生成 html
- javascript - 对登录不起作用的 onChange 函数做出反应
- spring-boot - 违反了获取 JPA 错误完整性约束 (FK_XXXXX) - 未找到父键
- flask - 烧瓶编辑方法不起作用,找不到原因
- arrays - 如何隔离对象的 id 以显示与该 id 相关的指定数据?
- powerbi - 如何在 PowerBi 中为比赛结果建模数据?
- android - 改造自定义超时无法正常工作