TypeORM一对一关联未定义 查询结果缺失关联字段如何解决
问题根因
- TypeORM 默认不会自动加载关联关系,直接调用
findOne未指定关联加载规则时,仅会返回当前实体的自有表字段,关联属性全部为undefined,这就是返回值缺失关联字段的直接原因。 - 双向
OneToOne关联配置不规范,未明确关系持有方,框架无法识别关联映射,会抛出关联未定义的错误。 - 实体属性命名不规范:将关联对象属性直接命名为数据库字段名
user_id,混淆了实体属性和数据库表字段的边界;更新逻辑参数传值错误,直接将整个实体对象作为ID参数传入更新条件,类型完全不匹配。
修复方案
1. 修正实体关联配置
首先调整两个实体的关联定义,明确关系持有方,规范属性命名:
- 修正
MaintenanceDone实体:关联属性使用驼峰命名,数据库字段名仅在@JoinColumn的name字段声明,不要直接用字段名作为关联对象的属性名
@Entity() export class MaintenanceDone extends BaseEntity{ @PrimaryGeneratedColumn({type: 'bigint'}) id: number; @ManyToOne(()=> User, user => user.maintenanceDone, { onDelete: 'SET NULL' }) @JoinColumn({name: "user_id"}) user: User; // 原命名user_id错误,这是关联的User实体对象,不是ID值 // 该表持有外键,是OneToOne关系的持有方 @OneToOne(()=> MaintenanceNeed, maintenanceNeed => maintenanceNeed.maintenanceDone) @JoinColumn({name: 'maintenance_need_id'}) maintenanceNeed: MaintenanceNeed; @Column({type: "bigint"}) time_spent: number; }
- 修正
MaintenanceNeed实体的反向OneToOne配置,明确指向对方的关联属性,非持有方不需要加@JoinColumn
@Entity() export class MaintenanceNeed extends BaseEntity{ @PrimaryGeneratedColumn({type: 'bigint'}) id: number; @OneToOne(() => Ticket, ticket => ticket.maintenanceNeed, { cascade : true }) @JoinColumn({name: 'ticket_id'}) ticket : Ticket; @ManyToOne(()=> House, house => house.maintenanceNeed) @JoinColumn({name: 'house_id'}) house: House; @ManyToOne(()=> MaintenanceNeedTemplate, maintenanceNeedTemplate => maintenanceNeedTemplate.maintenanceNeed) @JoinColumn({name: 'template_id'}) maintenanceNeedTemplate : MaintenanceNeedTemplate; // 反向关联,明确映射到MaintenanceDone实体的maintenanceNeed属性 @OneToOne(()=> MaintenanceDone, maintenanceDone => maintenanceDone.maintenanceNeed) maintenanceDone: MaintenanceDone; @Column({type: 'boolean', default: true}) is_to_do: boolean; }
2. 查询时显式加载关联
TypeORM 不会隐式加载关联,查询时必须明确声明需要加载的关联关系,两种写法均可:
- Repository 写法:
const toDelete = await this.maintenanceDoneRepository.findOne({ where: { id: id }, // 声明需要加载的关联属性 relations: ['user', 'maintenanceNeed'] });
- QueryBuilder 写法:
const toDelete = await getConnection().createQueryBuilder() .from(MaintenanceDone, "md") // 手动关联查询需要的关联实体 .leftJoinAndSelect("md.user", "user") .leftJoinAndSelect("md.maintenanceNeed", "maintenanceNeed") .where("md.id = :id" ,{id : id}) .getOne();
如果只需要外键ID值,不需要加载整个关联实体,可以直接查询原始字段,性能更高:
const toDelete = await getConnection().createQueryBuilder() .select(["md.id", "md.user_id", "md.maintenance_need_id"]) .from(MaintenanceDone, "md") .where("md.id = :id" ,{id : id}) .getRawOne();
3. 修正更新逻辑参数
更新MaintenanceNeed时不要传入整个toDelete对象,取对应关联的ID值作为查询条件:
await getConnection().createQueryBuilder() .update(MaintenanceNeed) // 传入关联的维修需求ID,如果是用getRawOne查询则取toDelete.md_maintenance_need_id .where("id = :id" ,{ id : toDelete.maintenanceNeed.id }) .set({ is_to_do : true }) .execute();
内容的提问来源于stack exchange,提问作者Mauro De Stefano
相关产品推荐
相关产品推荐

