midwayjs 里如何更好的使用typeorm的事务

601 阅读1分钟

背景

拿最简单的一个例子举例,用户注册成功后,还会为用户生成一个钱包

typeorm 官方文档里提供了两种使用事务的方式:typeorm.io/transaction…

我们可以很简单的在一个service里使用事务,来达到我们的需求。

// user.service.ts

const dataSource = this.dataSourceManager.getDataSource("default");  
await dataSource.transaction(async (transactionalEntityManager) => {  
    await transactionalEntityManager.save(UserEntity, new UserEntity());  
    await transactionalEntityManager.save(WalletEntity, new WalletEntity());  
});

但是实际开发过程中,往往没有这么简单,有以下两种情况

  1. 事务里调用了其他的方法,而这个方法里有操作数据库
// user.service.ts

const dataSource = this.dataSourceManager.getDataSource("default");  
await dataSource.transaction(async (transactionalEntityManager) => {  
    await transactionalEntityManager.save(UserEntity, new UserEntity());  
    await this.walletService.createWallet();
});
  1. 一个方法里调用了多个方法,这多个方法里都有操作数据库
// user.controller.ts

await this.userService.createUser()
await this.walletService.createWallet();

解决办法1: 避免出现上述两种情况

也就是说 需要保证事务性的,全都放在一个方法里。类似下面这样

// user.service.ts

const dataSource = this.dataSourceManager.getDataSource("default");  
await dataSource.transaction(async (transactionalEntityManager) => {  
    await transactionalEntityManager.save(UserEntity, new UserEntity());  
    // await this.walletService.createWallet();
    // 改为直接操作数据库
    await transactionalEntityManager.save(WalletEntity, new WalletEntity());  
});

但是只能适用于简单场景

解决办法2: 通过调整业务顺序

比如上面这个场景,我们可以先创建钱包,再把钱包id保存到user表里,假设钱包创建成功,用户创建失败,那也只会多一个空钱包而已

解决办法3: 把 transactionalManager 存储到ctx中

此思路参考:github.com/go-kratos/e…

我们可以这样做

// transaction_manager.ts

import { Inject, Provide } from "@midwayjs/core";  
import { EntityManager } from "typeorm";  
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";  
import { Context } from "@midwayjs/koa";  
  
@Provide()  
export class TransactionManager {  
    @Inject()  
    dataSourceManager: TypeORMDataSourceManager;  

    @Inject()  
    ctx: Context;  
  
    async inTx(func) {  
        const dataSource = this.dataSourceManager.getDataSource('default');  
        return await dataSource.transaction(async transactionalEntityManager => {  
            this.ctx.entityManager = transactionalEntityManager;  
            await func();  
        });  
    }  

    db(): EntityManager {  
        if (this.ctx.entityManager) {  
            return this.ctx.entityManager;  
        }  
        const dataSource = this.dataSourceManager.getDataSource('default');  
        return dataSource.manager;  
    }  
}

使用

@Inject()  
tm: TransactionManager;

....

await this.tm.inTx(async () => {  
    await this.userService.createUser()
    await this.walletService.createWallet();  
});

....
// user.service.ts 中创建用户
await this.tm.db().save(UserEntity, user);