TypeORM事务EntityManager未提交部分数据问题排查
TypeORM事务EntityManager异常:Buzz未持久化但Fizz已关联buzzId的问题
代码片段
class Fizz extends BaseEntity { ... buzzId: number; } class Buzz extends BaseEntity { ... } class FizzService { createFizz(input) { return Fizz.create(...input); } } class BuzzService { createBuzz(input) { return Buzz.create(...input); } } class FizzResolver { async generate(payload) { await dataSource.transaction( async (transactionalEntityManager) => { let fizz = fizzService.createFizz(payload.fizzInput); const buzz = buzzService.createBuzz(payload.buzzInput); await transactionalEntityManager.save(buzz); // 有时能持久化数据,有时不能 fizz = Object.assign(fizz, { buzzId: buzz.id }); await transactionalEntityManager.save(fizz); } ); } }
问题描述
预期事务中任何错误都会触发回滚,不会提交任何数据,但实际情况是:有时Buzz实体记录未被持久化,但Fizz实体记录中却存在生成的buzzId。
问题原因及解决方案
核心原因
- 实体未绑定事务上下文:
Buzz.create()是BaseEntity的静态方法,默认使用全局EntityManager而非事务内的transactionalEntityManager。调用transactionalEntityManager.save(buzz)时,TypeORM需要将游离态实体关联到事务上下文,这个过程可能因状态异常导致save(buzz)失败,但buzz.id已提前生成(比如自增ID在INSERT时被分配,即使事务回滚ID也不会回收)。 - 主键生成与事务回滚的时序差:对于自增主键,
save(buzz)执行时会先获取自增ID并赋值给buzz.id,若后续save(fizz)成功后事务因其他原因回滚,Buzz记录会被回滚,但Fizz记录可能已提交(取决于错误触发时机),导致Fizz关联了不存在的Buzz ID。 - 异步操作未正确await:若事务内存在未被
await的异步操作,可能导致事务提前提交或异常未被捕获,出现部分数据留存的情况。
解决方案
使用事务EntityManager创建实体
修改服务层方法,允许传入事务内的EntityManager,确保实体从创建时就绑定到事务上下文:class FizzService { createFizz(input, entityManager) { return entityManager.create(Fizz, input); } } class BuzzService { createBuzz(input, entityManager) { return entityManager.create(Buzz, input); } } // 事务内调用 async generate(payload) { await dataSource.transaction( async (transactionalEntityManager) => { let fizz = fizzService.createFizz(payload.fizzInput, transactionalEntityManager); const buzz = buzzService.createBuzz(payload.buzzInput, transactionalEntityManager); await transactionalEntityManager.save(buzz); fizz.buzzId = buzz.id; await transactionalEntityManager.save(fizz); } ); }确保所有异步操作都被await
检查事务回调内的所有异步逻辑,必须用await包裹,避免异步操作脱离事务控制。显式捕获并传播异常
在事务回调内添加try-catch块,确保异常被正确抛出,触发事务回滚:async generate(payload) { await dataSource.transaction( async (transactionalEntityManager) => { try { let fizz = fizzService.createFizz(payload.fizzInput, transactionalEntityManager); const buzz = buzzService.createBuzz(payload.buzzInput, transactionalEntityManager); await transactionalEntityManager.save(buzz); fizz.buzzId = buzz.id; await transactionalEntityManager.save(fizz); } catch (err) { throw err; // 确保异常传播,触发回滚 } } ); }
内容的提问来源于stack exchange,提问作者Oneb
相关产品推荐
相关产品推荐

