项目中ChainedTransactionManager已标记@Deprecated,求双数据库链式事务替代方案
替代ChainedTransactionManager的多数据库事务方案
方案1:手动编排多个TransactionTemplate
直接用对应两个数据库的TransactionTemplate手动控制事务执行顺序,完全复刻ChainedTransactionManager的链式提交/回滚逻辑:
@Service public class MultiDbService { private final TransactionTemplate db1TxTemplate; private final TransactionTemplate db2TxTemplate; private final Db1Dao db1Dao; private final Db2Dao db2Dao; // 构造注入两个数据库的事务管理器,初始化对应的TransactionTemplate public MultiDbService(@Qualifier("db1TxManager") PlatformTransactionManager db1TxManager, @Qualifier("db2TxManager") PlatformTransactionManager db2TxManager, Db1Dao db1Dao, Db2Dao db2Dao) { this.db1TxTemplate = new TransactionTemplate(db1TxManager); this.db2TxTemplate = new TransactionTemplate(db2TxManager); this.db1Dao = db1Dao; this.db2Dao = db2Dao; } public void executeCrossDbOperation() { // 先执行第一个数据库事务 db1TxTemplate.execute(status1 -> { db1Dao.saveEntity(...); // 嵌套执行第二个数据库事务,若DB2操作失败,会先回滚DB2再回滚DB1 db2TxTemplate.execute(status2 -> { db2Dao.saveEntity(...); return null; }); return null; }); } }
这种方式完全手动控制事务流转,和ChainedTransactionManager的行为一致:失败时按反向顺序回滚,成功时按开启顺序提交。
方案2:自定义复合事务管理器
如果需要复用链式事务逻辑,可以自己实现一个轻量的复合事务管理器,替代已弃用的ChainedTransactionManager:
public class CustomChainedTxManager extends AbstractPlatformTransactionManager { private static final Logger logger = LoggerFactory.getLogger(CustomChainedTxManager.class); private final List<PlatformTransactionManager> transactionManagers; public CustomChainedTxManager(List<PlatformTransactionManager> transactionManagers) { this.transactionManagers = transactionManagers; } @Override protected Object doGetTransaction() { List<Object> transactions = new ArrayList<>(); // 依次开启每个数据库的事务 for (PlatformTransactionManager tm : transactionManagers) { transactions.add(tm.getTransaction(new DefaultTransactionDefinition())); } return transactions; } @Override protected void doBegin(Object transaction, TransactionDefinition definition) { // doGetTransaction中已完成事务开启,此处无需额外操作 } @Override protected void doCommit(DefaultTransactionStatus status) { List<Object> transactions = (List<Object>) status.getTransaction(); // 按开启顺序提交事务 for (int i = 0; i < transactionManagers.size(); i++) { transactionManagers.get(i).commit(new DefaultTransactionStatus( transactions.get(i), false, false, false, false, null)); } } @Override protected void doRollback(DefaultTransactionStatus status) { List<Object> transactions = (List<Object>) status.getTransaction(); // 按反向顺序回滚事务(避免先回滚早开启的事务,导致后续回滚失败) for (int i = transactionManagers.size() - 1; i >= 0; i--) { try { transactionManagers.get(i).rollback(new DefaultTransactionStatus( transactions.get(i), false, false, false, false, null)); } catch (Exception e) { logger.error("回滚第{}个事务管理器失败", i, e); } } } }
配置类中注册该管理器:
@Configuration public class TransactionConfig { @Bean public PlatformTransactionManager customChainedTxManager( @Qualifier("db1TxManager") PlatformTransactionManager db1TxManager, @Qualifier("db2TxManager") PlatformTransactionManager db2TxManager) { return new CustomChainedTxManager(Arrays.asList(db1TxManager, db2TxManager)); } }
之后就可以通过@Transactional(transactionManager = "customChainedTxManager")注解使用,和之前用ChainedTransactionManager的方式一致。
方案3:基于TransactionSynchronization的最终一致性方案
如果业务可以接受最终一致性,可利用事务同步回调,在第一个数据库事务提交后执行第二个数据库事务:
@Service public class MultiDbService { private final PlatformTransactionManager db1TxManager; private final PlatformTransactionManager db2TxManager; private final Db1Dao db1Dao; private final Db2Dao db2Dao; public void executeCrossDbOperation() { TransactionTemplate db1TxTemplate = new TransactionTemplate(db1TxManager); db1TxTemplate.execute(status -> { db1Dao.saveEntity(...); // 注册同步回调,DB1事务提交后执行DB2操作 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { TransactionTemplate db2TxTemplate = new TransactionTemplate(db2TxManager); db2TxTemplate.execute(status2 -> { db2Dao.saveEntity(...); return null; }); } }); return null; }); } }
注意:这种方式下DB2事务失败时,DB1已经提交无法回滚,仅适用于允许最终一致的业务场景。
以上方案均属于本地事务的链式编排,和ChainedTransactionManager一样不提供跨数据库的强原子性(区别于JTA分布式事务),符合你排除JTA方案的需求。
内容的提问来源于stack exchange,提问作者Дмитрий Кутявин
相关产品推荐
相关产品推荐

