TypeORM级联插入外键异常:重复插入Sender触发唯一约束报错
问题:级联插入时重复触发Sender唯一约束报错
我有两个实体Sender(一)和Transfer(多),存在ManyToOne/OneToMany关联关系,且Transfer的sender字段启用了级联插入。尝试插入两个归属同一Sender的Transfer时,第二个插入操作触发了Sender地址的唯一约束报错。
实体代码
@Entity() export class Transfer extends BaseEntity { // Each transfer has an id and the sender @PrimaryGeneratedColumn() id!: number; @ManyToOne(() => Sender, (sender) => sender.sends, { nullable: false, eager: true, cascade: ["insert"] }) sender!: Sender } @Entity() export class Sender extends BaseEntity { // Each address has an id and a value of the address @PrimaryGeneratedColumn() id!: number; @Column({ unique: true, nullable: false }) address!: string; @OneToMany(() => Transfer, (transfer) => transfer.sender, { eager: false, cascade: false }) sends!: Transfer[] }
操作代码
// get typeorm EntityManager const em = dataSource.manager const a = 'test_address' // save the first transfer - cascade will also insert the sender await em.save(Transfer, { sender: { address: a}}) // save a new transfer by the same sender const s = await em.findOneBy(Sender, { address: a }) await em.save(Transfer, { sender: s })
解决方案
核心原因
TypeORM的save方法判断是否插入新实体的逻辑是:如果传入的实体对象包含主键(id)且处于EntityManager的托管状态,则只会执行关联操作,不会触发插入;如果传入的是无主键的对象,则会被当作新实体尝试插入。
可行解决方式
- 确保传入托管状态的Sender实体
通过findOneBy查询到的Sender已经是EntityManager托管的实体,直接关联到Transfer后执行save即可,TypeORM会自动识别这是已有实体,不会重复插入。如果仍报错,可手动构造包含主键的Sender对象:
const a = 'test_address'; await em.save(Transfer, { sender: { address: a }}); const s = await em.findOneBy(Sender, { address: a }); // 手动传入带主键的Sender对象 await em.save(Transfer, { sender: { id: s.id, address: s.address } });
- 移除级联插入,手动维护关联
如果业务中Sender通常预先存在,可移除Transfer上的级联插入配置,改为手动确保Sender存在后再关联:
修改Transfer的实体定义:
@ManyToOne(() => Sender, (sender) => sender.sends, { nullable: false, eager: true // 移除 cascade: ["insert"] }) sender!: Sender
操作代码改为:
const a = 'test_address'; // 先查询或创建Sender let s = await em.findOneBy(Sender, { address: a }); if (!s) { s = await em.save(Sender, { address: a }); } // 插入两个Transfer await em.save(Transfer, { sender: s }); await em.save(Transfer, { sender: s });
- 使用
getReference创建实体引用
如果不想查询完整的Sender实体,可通过getReference创建仅包含主键的代理实体,TypeORM会识别为已有实体:
const a = 'test_address'; await em.save(Transfer, { sender: { address: a }}); const s = await em.findOneBy(Sender, { address: a }); const senderRef = em.getReference(Sender, s.id); await em.save(Transfer, { sender: senderRef });
内容的提问来源于stack exchange,提问作者sev
相关产品推荐
相关产品推荐

