这是我的实体。
// 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”
我阅读了有关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'
经过一些实验,我注意到当指定令牌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字段是一个索引,并且也是唯一的。
可以选择什么,为什么我的令牌没有更新?
答案 0 :(得分:1)
整个问题是Repository<T>.save()
函数行为的结果。
根据the 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();
也许,您的另一选择是使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;
希望有帮助:)
答案 1 :(得分:0)
并不是说表中已经有一个重复的条目,而是说其中已经有一个条目具有该主键的值,并且出于这个原因它拒绝插入第二个条目。 / p>
您将找到一个匹配的行,并且错误时的代码正试图插入第二行。
处理插入的重复项
如果您尝试为主键(或唯一索引)插入重复的值,则始终会出现该错误。有两种解决方法:插入之前检查并进行更新(如果可能已更改)或什么都不做。
答案 2 :(得分:0)
您可以使用 .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,
};
}
}
}