nestjs / TypeOrm 数据库事务

sup*_*123 10 typeorm nestjs

假设我们有 2 个服务,A 和 B。服务 A 具有执行以下操作的功能:

  1. 验证数据
  2. 调用服务 B 函数,对数据库进行更改
  3. 多做点事
  4. 对数据库进行更改

现在,让我们假设以下步骤 3 或 4 之一失败。由于服务 B 在数据库中进行了更改,因此这些更改仍然存在。

在这种情况下,有没有办法回滚数据库?我虽然是关于数据库事务的,但是我在 nest js 中找不到任何方法来做到这一点,尽管 TypeOrm 支持它,但嵌套看起来并不自然。如果没有,我现在“卡住”了服务 B 发生的更改,但没有更改应该由 A 发生。

非常感谢。

Žel*_*vić 13

typeorm-transactional使用 CLS(连续本地存储)来处理和传播不同存储库和服务方法之间的事务。

@Injectable()
export class PostService {
  constructor(
    private readonly authorRepository: AuthorRepository,
    private readonly postRepository: PostRepository,
  ) {}

  @Transactional() // will open a transaction if one doesn't already exist
  async createPost(authorUsername: string, message: string): Promise<Post> {
    const author = await this.authorRepository.create({ username: authorUsername });
    return this.postRepository.save({ message, author_id: author.id });
  }
}
Run Code Online (Sandbox Code Playgroud)


zen*_*eni 12

有很多解决方案,它们应该都是基于SQL事务管理的。

我个人认为,实现这一目标的最简单方法是EntityManager在数据库上执行代码时使用相同的实例。然后你可以使用类似的东西:

getConnection().transaction(entityManager -> {
    service1.doStuff1(entityManager);
    service2.doStuff2(entityManager);
});
Run Code Online (Sandbox Code Playgroud)

如果您在 ORM 操作之外执行原始 SQL,您可以QueryRunnerEntityManager将包装在同一事务中的实例中生成。您还需要从中生成Repository实例,EntityManager否则它们将在主事务之外执行代码。

  • 虽然这是一个解决方案,但有一个明显的问题:每个服务方法都必须接受一个 EntityManager 参数。如果这些服务使用其他可注入对象,例如数据访问对象 (DAO),则服务方法将需要将 EntityManager 实例进一步向下传递(例如传递到 DAO 方法)。我认为这是一个明显且冲动的解决方案——一个有效的解决方案!——但它增加了大量的样板代码并且不适合 DI 和 IoC。 (14认同)
  • 我个人保留参数并在方法中使用装饰器来注入新的实体管理器(如果它为空),所以我永远不必担心初始化它。对我来说,保持明确的事务管理比代码糖更重要,我个人不认为它是样板文件。 (2认同)

nat*_*l93 6

这是我解决它的方法,因为我需要使用悲观锁。

我觉得这是做事的“巢”方式,因为你可以简单地要求NestJS注入一个 Typeorm 的实例,Connection你很高兴。

@Injectable()
class MyService {
  // 1. Inject the Typeorm Connection
  constructor(@InjectConnection() private connection: Connection) { }

  async findById(id: number): Promise<Thing> {
    return new Promise(resolve => {
      // 2. Do your business logic
      this.connection.transaction(async entityManager => {
        resolve(
          await entityManager.findOne(Thing, id, {
            lock: { mode: 'pessimistic_write' },
          }),
        );
      });
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

只需将您需要的任何其他逻辑放在.transaction块中,您就可以开始了。

NOTE: You MUST use the entityManager provided by the .transaction method or else it will not work.