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

如何实现NestJS的@Transactional()装饰器以在失败时回滚数据库变更

实现NestJS事务装饰器@Transactional()

要实现一个在方法执行失败时自动回滚数据库变更的@Transactional()装饰器,我们可以基于TypeORM的事务机制构建,以下是具体实现步骤:

1. 创建Transactional装饰器

在src/decorators/transactional.decorator.ts中编写装饰器代码:

import { DataSource } from 'typeorm';

export function Transactional() {
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    const originalMethod = descriptor.value;

    descriptor.value = async function (...args: any[]) {
      const dataSource: DataSource = target.dataSource;
      const queryRunner = dataSource.createQueryRunner();

      try {
        await queryRunner.connect();
        await queryRunner.startTransaction();

        // 将事务专属EntityManager附加到服务实例
        const originalManager = this.manager;
        this.manager = queryRunner.manager;

        // 执行原方法逻辑
        const result = await originalMethod.apply(this, args);

        await queryRunner.commitTransaction();
        return result;
      } catch (error) {
        await queryRunner.rollbackTransaction();
        throw error;
      } finally {
        await queryRunner.release();
        this.manager = originalManager;
      }
    };

    return descriptor;
  };
}

2. 修改服务类,注入DataSource并使用装饰器

确保服务类注入TypeORM的DataSource,并且方法内使用事务专属的EntityManager操作数据库(避免使用默认Repository的全局EntityManager):

import { Inject, Injectable } from '@nestjs/common';
import { DataSource, Repository } from 'typeorm';
import { Brand } from './brand.entity';
import { BRANDS_REPOSITORY } from './brands.repository';
import { USERS_REPOSITORY } from '../users/users.repository';
import { User } from '../users/user.entity';
import { DATA_SOURCE } from '../database/database.providers';
import { Transactional } from '../../decorators/transactional.decorator';

@Injectable()
export class BrandsService {
  constructor(
    @Inject(BRANDS_REPOSITORY) private readonly brandsRepository: Repository<Brand>,
    @Inject(USERS_REPOSITORY) private readonly usersRepository: Repository<User>,
    @Inject(DATA_SOURCE) private readonly dataSource: DataSource,
  ) {}

  @Transactional()
  async create(user: User, name: string) {
    // 使用事务内的EntityManager获取Repository
    const transactionalBrandRepo = this.manager.getRepository(Brand);
    let brand = transactionalBrandRepo.create({ name });
    brand = await transactionalBrandRepo.save(brand);

    const transactionalUserRepo = this.manager.getRepository(User);
    user.permittedBrands.push(brand);
    await transactionalUserRepo.save(user);

    // 抛出错误测试回滚
    throw new Error('Something went wrong');

    return brand;
  }
}

核心要点

  • 事务上下文绑定:装饰器创建专属QueryRunner并启动事务,将事务内的EntityManager附加到服务实例,确保所有数据库操作在同一事务中执行。
  • 自动回滚:方法抛出错误时,装饰器立即执行事务回滚,撤销所有已完成的数据库变更。
  • 资源清理:无论事务成功或失败,都会释放QueryRunner资源,避免数据库连接泄漏。

内容的提问来源于stack exchange,提问作者ibodi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 07:26:18