如何更新实体(如果存在)或创建(如果不存在)实体
How update entity if exist or create if not exist entity
这是我的实体。
// 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: Duplicate entry '36' for key 'REL_d417e5d35f2434afc4bd48cb4d'
我阅读了有关保存方法的文档。但是为什么会报错,我不明白。我希望记录得到更新。为什么我的令牌详细信息没有更新?
我知道如何使用 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: Duplicate entry '36' for key 'REL_d417e5d35f2434afc4bd48cb4d'
我搜索并找到了一些示例。
1)
2) https://github.com/typeorm/typeorm/issues/3342
他们说值必须是主键或唯一值。但是我的userId字段是一个索引,也是唯一的。
可以有哪些选项,为什么我的token没有更新?
这并不是说 table 中已经有一个重复的条目,而是说那里已经有一个主键值为该值的条目并且它拒绝插入一个第二个原因。
您会找到一个匹配的行,并且您的代码在错误点试图插入第二行。
处理插入时的重复项:
如果您尝试为主键(或唯一索引)插入重复值,您将始终收到该错误。有几种解决方法:在插入之前进行检查,然后进行更新(如果某些内容可能已更改)或者什么都不做。
整个问题是 Repository<T>.save()
函数行为的结果。
根据 the docs,save()
函数具有以下行为:
Saves all given entities in the database. If entities do not exist in the database then inserts, otherwise updates.
但是,如果实体内部没有 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
字段设为 table 的主键。这将解决 save()
函数的更新插入问题。正如您所描述的,userId 字段是一个索引,它也是唯一的。因此,您可以轻松地将其设为主要字段。
这可以通过修改您的实体、删除 @PrimaryGeneratedId
并将用户 ID 设为 @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;
希望对您有所帮助:)
您可以使用 .save 进行更新和插入,或者检查是否存在然后 .update else .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,
};
}
}
}
这是我的实体。
// 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: Duplicate entry '36' for key 'REL_d417e5d35f2434afc4bd48cb4d'
我阅读了有关保存方法的文档。但是为什么会报错,我不明白。我希望记录得到更新。为什么我的令牌详细信息没有更新?
我知道如何使用 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: Duplicate entry '36' for key 'REL_d417e5d35f2434afc4bd48cb4d'
我搜索并找到了一些示例。
1)
2) https://github.com/typeorm/typeorm/issues/3342
他们说值必须是主键或唯一值。但是我的userId字段是一个索引,也是唯一的。
可以有哪些选项,为什么我的token没有更新?
这并不是说 table 中已经有一个重复的条目,而是说那里已经有一个主键值为该值的条目并且它拒绝插入一个第二个原因。
您会找到一个匹配的行,并且您的代码在错误点试图插入第二行。
处理插入时的重复项:
如果您尝试为主键(或唯一索引)插入重复值,您将始终收到该错误。有几种解决方法:在插入之前进行检查,然后进行更新(如果某些内容可能已更改)或者什么都不做。
整个问题是 Repository<T>.save()
函数行为的结果。
根据 the docs,save()
函数具有以下行为:
Saves all given entities in the database. If entities do not exist in the database then inserts, otherwise updates.
但是,如果实体内部没有 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
字段设为 table 的主键。这将解决 save()
函数的更新插入问题。正如您所描述的,userId 字段是一个索引,它也是唯一的。因此,您可以轻松地将其设为主要字段。
这可以通过修改您的实体、删除 @PrimaryGeneratedId
并将用户 ID 设为 @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;
希望对您有所帮助:)
您可以使用 .save 进行更新和插入,或者检查是否存在然后 .update else .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,
};
}
}
}