如何用NestJS TypeORM触发N+1查询问题?一对多关联场景未出现N+1问题的原因排查
嘿,我来帮你理清这个问题~你现在没遇到N+1是因为当前的查询写法本来就不会触发它,下面我分两部分给你解释:
为什么你的代码不会触发N+1查询
你在find和findOne方法里都传入了{ relations: ['employee'] }或{ relations: ['company'] },TypeORM在这种情况下会自动生成LEFT JOIN的SQL语句,一次性把主表和关联表的数据拉取完成。比如查询所有公司时,SQL大致是这样的:
SELECT * FROM COMPANY LEFT JOIN EMPLOYEE ON COMPANY.companyId = EMPLOYEE.companyId
这是单条查询,自然不会出现“1次查主表+N次查关联表”的N+1问题。
另外你提到尝试了懒加载和急加载,但可能没用到触发N+1的场景:
- 急加载(Eager):如果给关联设置
eager: true,TypeORM会自动在每次查询主实体时通过JOIN加载关联数据,同样是单条查询,不会N+1。 - 懒加载(Lazy):你当前的实体关联没有开启
lazy: true,就算开启了,只要用了relations选项,TypeORM还是会优先用JOIN加载,不会触发懒加载的额外查询。
如何在NestJS TypeORM中触发N+1查询
下面给你几种具体的场景,照着改代码就能复现N+1:
1. 不使用relations选项,手动遍历访问关联属性
修改CompanyService的getAllCompany方法,去掉relations配置:
getAllCompany() { return this.companyRepository.find(); // 只查询公司表,不加载员工关联 }
然后在调用这个方法后,遍历每个公司并访问employee属性:
// 比如在Controller里 const companies = await this.companyService.getAllCompany(); for (const company of companies) { // 每循环一次就会触发一条SELECT * FROM EMPLOYEE WHERE companyId = ? const employees = await company.employee; }
这时候就会出现:1次查询所有公司 + N次查询每个公司的员工,也就是典型的N+1问题。
2. 开启懒加载模式,且不提前加载关联
先修改Company实体的关联定义,开启懒加载:
@Entity('COMPANY') export class Company extends TimeStamped { // ... 其他属性 @OneToMany(() => Employee, (employee) => employee.company, { onDelete: 'CASCADE', lazy: true // 开启懒加载 }) employee: Promise<Employee[]>; // 类型要改成Promise }
然后查询时不用relations:
getAllCompany() { return this.companyRepository.find(); }
之后遍历访问employee属性,就会触发N次额外的员工查询。
3. 使用QueryBuilder但不做JOIN,后续访问关联
用QueryBuilder查询公司,但不关联员工表:
async getAllCompany() { const companies = await this.companyRepository .createQueryBuilder('company') .getMany(); // 遍历访问关联属性,触发N次查询 for (const company of companies) { await company.employee; } return companies; }
4. 从子实体查询,不关联父实体后遍历访问
修改EmployeeService的getAllEmployee方法,去掉relations:
getAllEmployee() { return this.employeeRepository.find(); // 只查询员工表,不加载公司关联 }
调用后遍历每个员工访问company属性:
const employees = await this.employeeService.getAllEmployee(); for (const emp of employees) { // 每个员工触发一次查询公司的SQL,N+1问题出现 const company = await emp.company; }
注意:如果之前给关联设置了
eager: true,要先把它改成false,或者在QueryBuilder里用setEagerLoad(false)禁用自动急加载,否则TypeORM还是会自动JOIN查询,不会触发N+1。
内容的提问来源于stack exchange,提问作者momo




