TypeORM闭包表结构Units实体如何同时获取完整层级树及关联关系
TypeORM 闭包表层级树关联多对多属性查询方案
1. 先确认实体定义符合要求
确保你的Unit实体已正确配置闭包表装饰器、层级关系、以及和Member的多对多关联:
import { Entity, Column, PrimaryGeneratedColumn, Tree, TreeChildren, TreeParent, ManyToMany, JoinTable } from 'typeorm'; import { Member } from './member.entity'; @Entity() @Tree("closure-table") // 指定闭包表存储层级 export class Unit { @PrimaryGeneratedColumn() id: number; @Column() name: string; // 层级关联字段 @TreeChildren() children: Unit[]; @TreeParent() parent: Unit; // 和Member的多对多关联 @ManyToMany(() => Member) @JoinTable() members: Member[]; }
2. 固定层级场景直接使用findTrees携带relations参数
如果你的Unit层级深度是固定的(比如最多3级:排-班-组),直接在findTrees的查询参数里指定需要关联的属性即可:
// TypeORM 0.2.x 写法 import { getTreeRepository } from 'typeorm'; const unitRepository = getTreeRepository(Unit); // TypeORM 0.3.x 写法替换为: // const unitRepository = dataSource.getTreeRepository(Unit); const fullTreeWithMembers = await unitRepository.findTrees({ // 根据你的层级深度配置关联,需要加载几层就写几层children.members relations: ['members', 'children.members', 'children.children.members'] });
3. 动态层级场景使用QueryBuilder查询
如果层级深度不固定,用自定义QueryBuilder关联闭包表和成员表,再调用getTrees自动组装结构:
const fullTreeWithMembers = await unitRepository .createQueryBuilder('unit') // 关联闭包表获取完整层级关系 .leftJoinAndSelect('unit.closureAncestors', 'closure') .leftJoinAndSelect('closure.ancestor', 'ancestor') // 关联所有Unit节点的成员 .leftJoinAndSelect('unit.members', 'member') // 按需筛选字段,避免数据冗余 .select(['unit.id', 'unit.name', 'member.id', 'member.name']) .getTrees();
4. 兼容备选方案:先查树再批量挂载关联
如果遇到版本兼容问题导致上述方案不生效,可以手动做二次查询挂载:
// 1. 查询原始层级树 const rawTrees = await unitRepository.findTrees(); // 2. 递归提取所有树节点ID const getAllUnitIds = (units: Unit[]): number[] => { return units.reduce((ids, unit) => [...ids, unit.id, ...getAllUnitIds(unit.children || [])], []); }; const allUnitIds = getAllUnitIds(rawTrees); // 3. 批量查询所有Unit对应的成员 const unitsWithMembers = await unitRepository .createQueryBuilder('unit') .leftJoinAndSelect('unit.members', 'member') .whereInIds(allUnitIds) .getMany(); // 4. 把成员映射到原树节点 const memberMap = new Map(unitsWithMembers.map(u => [u.id, u.members])); const mountMembers = (units: Unit[]) => { units.forEach(unit => { unit.members = memberMap.get(unit.id) || []; unit.children?.length && mountMembers(unit.children); }); }; mountMembers(rawTrees); // rawTrees即为带成员的完整层级树
注意如果成员数据量较大,建议在join查询时指定select字段仅返回需要的属性,降低查询开销。
内容的提问来源于stack exchange,提问作者Samblues
相关产品推荐
相关产品推荐

