You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NestJS服务中调用其他服务时实现事务?代码修复求助

修复NestJS跨服务事务控制问题

你的代码存在几个核心问题导致事务不生效,以下是具体修复方案:

核心问题分析

  1. 操作未绑定事务连接:sleeveService.deleteForPortfolio 和 portfoliosRepository.update 都使用了默认数据源连接,而非你创建的 queryRunner 连接,导致这些操作不在事务范围内。
  2. 异步操作未处理:portfoliosRepository.update 未添加 await,会导致事务提前提交。
  3. 变量未定义:代码中使用的 portfolio 变量未从 updatePortfolioDto 转换而来。
  4. 错误未向上传递:catch块捕获错误后未重新抛出,调用方无法感知事务失败。
  5. 返回值类型错误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 21:12:50