TypeORM单实体双外键指向同表的保存异常排查
现有两个实体模型Match和Partner,Match包含两个指向Partner的引用(partner1和partner2),实体代码如下:
@Entity("partner") export class PartnerEntity { @PrimaryGeneratedColumn("uuid") id: string @Column() text: string } @Entity("match") export class MatchEntity { @PrimaryGeneratedColumn("uuid") id: string @ManyToOne(() => PartnerEntity, partner => partner.id) @JoinColumn() partner1?: PartnerEntity @ManyToOne(() => PartnerEntity, partner => partner.id) @JoinColumn() partner2?: PartnerEntity }
尝试通过TypeORM的Repository.save方法持久化MatchEntity对象:
this.matchRepository.save({ partner1: { text: "AAA" }, partner2: { text: "BBB" } })
但保存后partner1和partner2指向同一个Partner实体,数据库中外键值一致,返回结果如下:
{ id: "c58f3ea7-5002-463d-92e7-94d0c2992784", partner1: { id: "10978976-d120-4e48-a490-eba62e7c06e5", text: "AAA" }, partner2: { id: "10978976-d120-4e48-a490-eba62e7c06e5", text: "AAA" } }
需排查问题并提供可行解决方案,规避方案:改用ManyToMany关系、使用Repository.createQueryBuilder执行SQL语句。
@ManyToOne装饰器的第二个参数使用错误:该参数用于指定关联实体的反向关系属性(即PartnerEntity中指向MatchEntity的集合属性),而你错误地传入了partner => partner.id(实体的主键字段)。
当两个@ManyToOne都指定了相同的错误反向关系时,TypeORM的实体管理器会将两个新创建的Partner实例判定为同一个对象,最终只创建一条Partner记录,导致match表的两个外键都指向该记录。
方案1:修正@ManyToOne的反向关系参数
由于当前PartnerEntity未定义反向的OneToMany关系,直接省略@ManyToOne的第二个参数(或显式传入undefined)即可:
@Entity("match") export class MatchEntity { @PrimaryGeneratedColumn("uuid") id: string @ManyToOne(() => PartnerEntity) @JoinColumn() partner1?: PartnerEntity @ManyToOne(() => PartnerEntity) @JoinColumn() partner2?: PartnerEntity }
修改后,TypeORM会正确识别两个独立的ManyToOne关联,保存时会创建两条Partner记录,并分别关联到match的两个外键。
方案2:显式创建并保存Partner实体
先独立创建并保存两个Partner,再关联到Match,这种方式更直观,也能避免TypeORM自动关联处理的潜在问题:
// 先创建并保存两个Partner const partner1 = await this.partnerRepository.save({ text: "AAA" }); const partner2 = await this.partnerRepository.save({ text: "BBB" }); // 再保存关联好的Match await this.matchRepository.save({ partner1, partner2 });
内容的提问来源于stack exchange,提问作者João Pacheco

