如何在NestJS+TypeORM中实现多仓库事务化插入操作?
解决方案
针对你的场景,有两种可行的方案实现事务控制,无需大规模重构现有代码:
方案一:给Service方法添加EntityManager参数(最小改动)
修改TableService和ChairService的create方法,支持传入可选的EntityManager,让事务上下文里的所有操作复用同一个EntityManager,而非各自独立创建Repository:
修改table.service.ts
// import statements export class TableService { private readonly tableRepository: Repository<TableEntity>; constructor( @Inject('CONNECTION') private connection: DataSource, ) { this.tableRepository = connection.getRepository(TableEntity); } // 添加可选的manager参数 async create(createTableDto: CreateTable, manager?: EntityManager) { // 优先使用传入的manager获取Repository,否则用默认实例 const repo = manager ? manager.getRepository(TableEntity) : this.tableRepository; const result = await repo.insert({...createTableDto}); return repo.findOneOrFail({where: {id: result.identifiers[0].id}}); } }
ChairService做完全相同的修改即可。
修改test.service.ts
在TestService的create方法中,用connection.transaction包裹操作,并将事务内的EntityManager传给两个Service的create方法:
export class TestService { constructor( private readonly tableService: TableService, private readonly chairService: ChairService, @Inject('CONNECTION') private connection: DataSource, ) {} async create() { const tableData = ...; const chairData = ...; // 用connection.transaction包裹事务逻辑 return this.connection.transaction(async (manager) => { await this.tableService.create(tableData, manager); await this.chairService.create(chairData, manager); }); } }
这种方式改动极小,不影响原有单个API的调用逻辑,是最轻量化的实现方式。
方案二:使用QueryRunner手动管理事务
如果不想修改Service的方法签名,可以用QueryRunner手动控制事务生命周期,让所有操作绑定到同一个QueryRunner实例:
修改test.service.ts
export class TestService { constructor( private readonly tableService: TableService, private readonly chairService: ChairService, @Inject('CONNECTION') private connection: DataSource, ) {} async create() { const tableData = ...; const chairData = ...; // 创建QueryRunner并开启事务 const queryRunner = this.connection.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { // 从QueryRunner获取绑定事务的Repository const tableRepo = queryRunner.manager.getRepository(TableEntity); const chairRepo = queryRunner.manager.getRepository(ChairEntity); // 执行插入逻辑(若不想重复代码,可给Service添加接受Repository的方法) const tableResult = await tableRepo.insert({...tableData}); await tableRepo.findOneOrFail({where: {id: tableResult.identifiers[0].id}}); const chairResult = await chairRepo.insert({...chairData}); await chairRepo.findOneOrFail({where: {id: chairResult.identifiers[0].id}}); await queryRunner.commitTransaction(); } catch (err) { // 出错时回滚事务 await queryRunner.rollbackTransaction(); throw err; } finally { // 释放QueryRunner资源 await queryRunner.release(); } } }
如果不想在TestService里重复编写插入逻辑,可以给Table/ChairService新增接受Repository参数的方法,进一步复用原有业务代码。
内容的提问来源于stack exchange,提问作者s.k
相关产品推荐
相关产品推荐

