TypeORM启用Eager Load时如何按条件加载子实体
问题描述
我有Parent与Child两个实体,二者为OneToMany关联关系且已启用eager加载。当前查询Parent实体时会返回所有子实体,我期望仅加载deleted属性为false的Child子实体。尝试修改findAll方法的查询条件为{ where: { deleted: false, children:{deleted:false} } }后,若父实体存在已删除的子实体,则该父实体无法被查询到,寻求可行解决方案。
实体与服务代码
Parent 实体
@Entity() export class Parent { @PrimaryGeneratedColumn() id: number; @Column({ default: false }) deleted: boolean; @OneToMany(() => Child, (child) => child.parent, { cascade: true, eager: true, }) children: Child[]; }
Child 实体
@Entity() export class Child{ @PrimaryGeneratedColumn() id: number; @Column({ default: false }) deleted: boolean; @ManyToOne(() => Parent, (parent) => parent.children) parent: Parent; }
原 ParentsService 代码
@Injectable() export class ParentsService { constructor( @InjectRepository(Parent) private parentRepository: Repository<Parent>, ) {} findAll(): Promise<Parent[]> { return this.parentRepository.find({ where: { deleted: false } }); } }
尝试过的错误写法
findAll(): Promise<Parent[]> { return this.parentRepository.find({ where: { deleted: false, children:{deleted:false} } }); }
解决方案
方案1:用 QueryBuilder 在数据库层面过滤子实体(推荐)
直接使用createQueryBuilder做左关联查询,在关联时过滤子实体,这样父实体不会因为存在已删除子实体而被排除,同时只加载未删除的子实体:
@Injectable() export class ParentsService { constructor( @InjectRepository(Parent) private parentRepository: Repository<Parent>, ) {} async findAll(): Promise<Parent[]> { return this.parentRepository .createQueryBuilder('parent') // 关联子实体并过滤未删除的记录 .leftJoinAndSelect('parent.children', 'child', 'child.deleted = :isDeleted', { isDeleted: false }) // 过滤未删除的父实体 .where('parent.deleted = :parentDeleted', { parentDeleted: false }) .getMany(); } }
方案2:内存层面过滤子实体(适合小数据量)
如果子实体数量不多,可以通过以下方式在内存中过滤:
方法A:添加 getter 过滤
修改Parent实体,新增一个getter来返回未删除的子实体:
@Entity() export class Parent { @PrimaryGeneratedColumn() id: number; @Column({ default: false }) deleted: boolean; @OneToMany(() => Child, (child) => child.parent, { cascade: true, eager: true, }) children: Child[]; // 仅返回未删除的子实体 get activeChildren(): Child[] { return this.children.filter(child => !child.deleted); } }
使用时直接调用parent.activeChildren获取过滤后的子实体。
方法B:用 @AfterLoad 自动过滤
通过TypeORM的@AfterLoad装饰器,在实体加载完成后自动过滤子实体:
@Entity() export class Parent { @PrimaryGeneratedColumn() id: number; @Column({ default: false }) deleted: boolean; @OneToMany(() => Child, (child) => child.parent, { cascade: true, eager: true, }) children: Child[]; @AfterLoad() filterActiveChildren() { this.children = this.children.filter(child => !child.deleted); } }
这种方式无需修改查询代码,加载后自动处理子实体列表。
内容的提问来源于stack exchange,提问作者Gregory Boutte
相关产品推荐
相关产品推荐

