我如何导致 NestJs TypeOrm 出现 n + 1 个问题?

How do I cause n + 1 problems with NestJs TypeOrm?

我正在研究 TypeOrm 并试图创建一个 N+1 问题,但它没有正常发生。公司和员工有 1:N 关系。

你能告诉我为什么 N + 1 没有引起任何问题吗?我试过设置 Lazy 和 Eager,但我一直在连续进行左连接,这样 n + 1 就不会引起问题。

实体

@Entity('COMPANY')
export class Company extends TimeStamped {
    @PrimaryGeneratedColumn('increment')
    companyId: number;

    @Column({ type: 'varchar' })
    companyName: string;

    @OneToMany(() => Employee, (employee) => employee.company, {
        onDelete: 'CASCADE'
    })
    employee: Employee[];
}

@Entity('EMPLOYEE')
export class Employee extends TimeStamped {
    @PrimaryGeneratedColumn('increment')
    employeeId: number;

    @Column({ type: 'varchar' })
    employeeName: string;

    @ManyToOne(() => Company, (company) => company.employee)
    @JoinColumn([{ name: 'companyId', referencedColumnName: 'companyId' }])
    company: Company;
}

杂物

@Injectable()
export class CompanyService {
    constructor(
        @InjectRepository(Company)
        private readonly companyRepository: Repository<Company>
    ) {}

    getAllCompany() {
        return this.companyRepository.find({ relations: ['employee'] });
    }

    getCompany(companyId: number) {
        return this.companyRepository.findOne(companyId, {
            relations: ['employee']
        });
    }

    setCompany(setComanyDto: SetCompanyDto) {
        return this.companyRepository.save(setComanyDto);
    }
}


@Injectable()
export class EmployeeService {
    constructor(
        @InjectRepository(Employee)
        private readonly employeeRepository: Repository<Employee>,

        @InjectRepository(Company)
        private readonly companyRepository: Repository<Company>
    ) {}

    getAllEmployee() {
        return this.employeeRepository.find({
            relations: ['company']
        });
    }

    getEmployee(employeeId: number) {
        return this.employeeRepository.findOne(employeeId, {
            relations: ['company']
        });
    }

    async setEmployee(setEmployeeDto: SetEmployeeDto) {
        const employee: Employee = new Employee();
        employee.employeeName = setEmployeeDto.employeeName;
        employee.company = await this.companyRepository.findOne(
            setEmployeeDto.companyId
        );

        return this.employeeRepository.save(employee);
    }
}

我相信你对什么是 N+1 问题很清楚。需要更清楚理解的可以查看this question

如果您使用 eager 加载,您将不会看到 N+1 问题,因为它在一个查询中加入了相关实体和 return 这两个实体。

如果您按照下面的方式指定 relations,您将不会再次看到 N+1 问题,因为它创建了一个连接查询并且 return 全部在 1 个查询中。

this.companyRepository.find({ relations: ['employee'] });

要创建 N+1 问题,

更新您的 Company 实体,如下所示:

@Entity('COMPANY')
export class Company extends TimeStamped {
  @PrimaryGeneratedColumn('increment')
  companyId: number;

  @Column({ type: 'varchar' })
  companyName: string;

  @OneToMany(() => Employee, (employee) => employee.company, {
    onDelete: 'CASCADE',
    lazy: true
  })
  employee: Promise<Employee[]>
}

在你的 CompanyService 中,创建一个新函数来模拟 N+1 问题,如下所示:

@Injectable()
export class CompanyService {
  async createNPlus1Problem() {
    // Query all companies (let's say you have N number of companies)
    // SELECT * FROM "COMPANY";
    const companies = this.companyRepository.find();

    // The following `for` loop, loops through all N number of 
    // companies to get the employee data of each
    for(company of companies) {
      // Query employees of each company
      // SELECT * FROM "EMPLOYEE" WHERE "companyId"=?;
      const employees = await company.employee;
    }
  }
}

因此在上面的示例中,您有 1 查询来获取公司数据。并且 N 查询以获取员工数据。因此 N+1 问题。


希望这能澄清您的问题。干杯!!!