双数据库分布式事务实现咨询:父事务异常时如何回滚子事务(禁用ChainedTransactionManager、Atomikos及Bitronix)
解决跨双数据库的分布式事务问题(无第三方分布式事务管理器)
我来帮你搞定这个问题!你当前遇到的核心痛点是:当DataServiceImpl的saveData方法抛出异常时,OrDataServiceImpl的事务已经提前提交了,导致第二个数据库的数据没法回滚。这是因为默认的@Transactional传播行为下,不同事务管理器的事务是完全独立的——第二个事务会在save方法执行完毕后立刻提交,根本等不到主事务后续的异常触发回滚。
既然不能用ChainedTransactionManager、Atomikos或Bitronix,我们可以用编程式事务控制手动管理两个事务的生命周期,确保两个数据库的操作要么同时提交,要么同时回滚。
解决方案代码实现
首先修改你的DataServiceImpl,去掉声明式事务注解,改用编程式事务控制:
public interface DataService { void saveData(); } @Service public class DataServiceImpl implements DataService { @Autowired private DataRepository dataRepository; @Autowired private OrDataRepository orDataRepository; // 注入两个数据库对应的事务管理器 @Autowired @Qualifier("transactionManager") private PlatformTransactionManager mainTxManager; @Autowired @Qualifier("orTransactionManager") // 这里替换成你第二个数据库的事务管理器Bean名称 private PlatformTransactionManager orTxManager; @Override public void saveData() { // 定义事务属性:设置回滚规则、隔离级别等 DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition(); txDefinition.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(RuntimeException.class))); txDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); // 可根据业务调整 // 手动开启两个事务 TransactionStatus mainTxStatus = mainTxManager.getTransaction(txDefinition); TransactionStatus orTxStatus = orTxManager.getTransaction(txDefinition); try { // 操作第一个数据库 Data data = new Data(); data.setCompKey(UUID.randomUUID().toString().substring(1,5)); data.setName("data"); dataRepository.save(data); // 操作第二个数据库(直接调用Repository,不再通过带@Transactional的Service) OrData orData = new OrData(); orData.setCompKey(UUID.randomUUID().toString().substring(1,5)); orData.setName("ordata"); orDataRepository.save(orData); // 所有操作成功,依次提交两个事务 mainTxManager.commit(mainTxStatus); orTxManager.commit(orTxStatus); } catch (RuntimeException e) { // 任一操作失败,回滚两个事务 if (!mainTxStatus.isCompleted()) { mainTxManager.rollback(mainTxStatus); } if (!orTxStatus.isCompleted()) { orTxManager.rollback(orTxStatus); } throw e; // 重新抛出异常,让上层感知失败 } } }
同时,你可以把OrDataServiceImpl的@Transactional注解去掉,因为我们已经在统一的方法里管理事务了,避免重复开启事务。
方案原理说明
- 手动控制事务生命周期:我们直接通过两个事务管理器开启事务,完全掌握提交/回滚的时机,不会出现某个事务提前提交的情况。
- 原子性保障:只有当两个数据库的保存操作都成功完成后,才会依次提交事务;只要任一环节抛出异常,就立刻回滚两个事务。
- 避免事务上下文冲突:不再通过跨Service的声明式事务调用,而是在同一个方法内完成两个数据库的操作,消除了不同事务管理器的上下文隔离问题。
注意事项
- 事务隔离级别可以根据你的业务需求调整,比如设置为
ISOLATION_SERIALIZABLE来应对并发场景。 - 异常处理要覆盖所有可能的失败情况(比如数据库连接异常、保存操作抛出的非RuntimeException),可以根据需要扩展catch块。
- 如果业务逻辑复杂,可以把事务管理的逻辑封装成工具类,减少重复代码。
内容的提问来源于stack exchange,提问作者Sangeetharaj
相关产品推荐
相关产品推荐

