如何在NestJS的TypeORM迁移中注入Service并执行业务逻辑?
TypeORM迁移中注入Service执行业务逻辑的问题
想要在TypeORM迁移中注入Service,基于业务逻辑执行数据迁移,编写了如下代码:
import { MigrationInterface, QueryRunner, Repository } from 'typeorm'; import { MyService } from '../../services/MyService.service'; import { MyEntity } from '../../entities/MyEntity.entity'; export class MyEntityMigration12345678 implements MigrationInterface { name = 'MyEntityMigration12345678'; constructor( private readonly myService: MyService, ) {} public async up(queryRunner: QueryRunner): Promise<void> { const myEntityRepository: Repository<MyEntity> = queryRunner.connection.getRepository<MyEntity>(MyEntity); const entities = await myEntityRepository.findBy({ myColumn: '', }); for (const entity of entities) { const columnValue = this.myService.getColumnValue(myEntity.id); // 注:此处存在笔误,应为`entity.id` await myEntityRepository.save({ ...entity, myColumn: columnValue, }); } } // ... }
遇到两个问题:
myService为undefined;myEntityRepository.findBy(.)执行卡住。
问题原因与解决方法
1. myService 为 undefined 的原因及解决
TypeORM迁移类由框架直接实例化,不会通过依赖注入(DI)容器解析构造函数参数,因此构造函数中声明的myService无法被自动填充。
解决方式:在up方法内手动初始化Service,并传入它所需的所有依赖(比如从QueryRunner获取对应的Repository)。假设MyService的构造函数依赖MyEntityRepository,修改如下:
public async up(queryRunner: QueryRunner): Promise<void> { const myEntityRepository: Repository<MyEntity> = queryRunner.connection.getRepository(MyEntity); // 手动初始化Service,传入其依赖的Repository const myService = new MyService(myEntityRepository); // 后续业务逻辑 }
2. 查询卡住与批量操作优化
findBy卡住:通常是因为一次性加载大量数据导致内存占用过高或数据库查询超时,建议分页分批处理数据。- 循环调用
save会频繁触发数据库写入,性能低下且容易引发锁表问题,改用update批量操作更高效。
优化后的完整迁移代码:
import { MigrationInterface, QueryRunner, Repository, Not } from 'typeorm'; import { MyService } from '../../services/MyService.service'; import { MyEntity } from '../../entities/MyEntity.entity'; export class MyEntityMigration12345678 implements MigrationInterface { name = 'MyEntityMigration12345678'; public async up(queryRunner: QueryRunner): Promise<void> { const myEntityRepository: Repository<MyEntity> = queryRunner.connection.getRepository(MyEntity); // 手动初始化Service const myService = new MyService(myEntityRepository); // 分页处理数据,避免一次性加载过多记录 const batchSize = 100; let offset = 0; let hasMore = true; while (hasMore) { const entities = await myEntityRepository.find({ where: { myColumn: '' }, take: batchSize, skip: offset, }); if (entities.length === 0) { hasMore = false; break; } // 批量执行更新操作 const updateTasks = entities.map(entity => { const columnValue = myService.getColumnValue(entity.id); return myEntityRepository.update(entity.id, { myColumn: columnValue }); }); await Promise.all(updateTasks); offset += batchSize; } } public async down(queryRunner: QueryRunner): Promise<void> { // 可选:编写回滚逻辑,将修改后的数据恢复原状 const myEntityRepository: Repository<MyEntity> = queryRunner.connection.getRepository(MyEntity); await myEntityRepository.update( { myColumn: Not('') }, { myColumn: '' } ); } }
额外注意事项
- 若
MyService依赖其他服务(如配置类、其他Repository),需手动传入这些依赖的实例,确保Service能正常工作。 - 迁移前务必在测试环境验证逻辑,避免直接操作生产数据导致不可逆损失。
- 若数据量极大,可进一步缩小批量大小,或在低峰期执行迁移。
内容的提问来源于stack exchange,提问作者mangei
相关产品推荐
相关产品推荐

