如何在NestJS服务中调用其他服务时实现事务?代码修复求助
修复NestJS跨服务事务控制问题
你的代码存在几个核心问题导致事务不生效,以下是具体修复方案:
核心问题分析
- 操作未绑定事务连接:
sleeveService.deleteForPortfolio和portfoliosRepository.update都使用了默认数据源连接,而非你创建的queryRunner连接,导致这些操作不在事务范围内。 - 异步操作未处理:
portfoliosRepository.update未添加await,会导致事务提前提交。 - 变量未定义:代码中使用的
portfolio变量未从updatePortfolioDto转换而来。 - 错误未向上传递:catch块捕获错误后未重新抛出,调用方无法感知事务失败。
- 返回值类型错误:
update方法返回的是UpdateResult对象,而非Portfolio实体。
修复后的代码
第一步:修改sleeveService.deleteForPortfolio方法
让它支持接收QueryRunner参数,确保删除操作绑定到事务连接:
// sleeve.service.ts async deleteForPortfolio(authContext: AuthContext, portfolioId: string, queryRunner: QueryRunner): Promise<void> { // 使用queryRunner的manager执行删除,而非默认repository await queryRunner.manager.delete(Sleeve, { portfolioId }); }
第二步:修复主服务的update方法
async update(authContext: AuthContext, id: string, updatePortfolioDto: UpdatePortfolioDto, isSaved = false): Promise<Portfolio> { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { // 1. 传入queryRunner,让删除操作加入事务 await this.sleeveService.deleteForPortfolio(authContext, id, queryRunner); // 2. 转换DTO为可更新的实体数据 const portfolioUpdateData = { ...updatePortfolioDto }; // 3. 使用queryRunner的manager执行更新,确保操作在事务内 await queryRunner.manager.update(Portfolio, id, portfolioUpdateData); // 4. 查询并返回更新后的实体(update方法不返回实体) const updatedPortfolio = await queryRunner.manager.findOneBy(Portfolio, { id }); await queryRunner.commitTransaction(); return updatedPortfolio; } catch (err) { await queryRunner.rollbackTransaction(); throw err; // 重新抛出错误,让调用方处理失败情况 } finally { await queryRunner.release(); } }
简化方案:使用@Transactional装饰器(TypeORM 0.3+)
如果你的TypeORM版本支持,可通过装饰器简化事务代码,无需手动管理QueryRunner:
import { Transactional } from 'typeorm-transactional'; // 在方法上添加装饰器,自动管理事务 @Transactional() async update(authContext: AuthContext, id: string, updatePortfolioDto: UpdatePortfolioDto, isSaved = false): Promise<Portfolio> { await this.sleeveService.deleteForPortfolio(authContext, id); await this.portfoliosRepository.update(id, updatePortfolioDto); return this.portfoliosRepository.findOneBy({ id }); }
注意:使用该装饰器需要在
TypeOrmModule.forRoot配置中启用事务支持,且所有服务方法需通过TypeORM的EntityManager执行操作。
内容的提问来源于stack exchange,提问作者Emeka Obasi
相关产品推荐
相关产品推荐

