如何在Spring中创建@Transaction控制双Oracle数据库事务回滚?
Spring Boot多Oracle数据库事务一致性实现
现有数据源配置
已完成第一个Oracle数据源(UniversContract)的配置,代码如下:
@Configuration @EnableTransactionManagement @EnableJpaRepositories(basePackages = "br.com.xyz.univers.ms.universcontractsync.repository.universcontract", entityManagerFactoryRef = "universContractEntityManagerFactory", transactionManagerRef= "universContractTransactionManager" ) public class UniversContractDatasourceConfiguration { @Bean @ConfigurationProperties("app.datasource.universcontract") public DataSourceProperties universContractDataSourceProperties() { return new DataSourceProperties(); } @Bean @ConfigurationProperties("app.datasource.universcontract.hikari") public DataSource universContractDataSource() { return universContractDataSourceProperties().initializeDataSourceBuilder() .type(HikariDataSource.class).build(); } @Bean(name = "universContractEntityManagerFactory") public LocalContainerEntityManagerFactoryBean universContractEntityManagerFactory( EntityManagerFactoryBuilder builder) { return builder .dataSource(universContractDataSource()) .packages("br.com.xyz.univers.ms.universcontractsync.models.universcontract") .build(); } @Bean(name = "universContractTransactionManager") public PlatformTransactionManager universContractTransactionManager( final @Qualifier("universContractEntityManagerFactory") LocalContainerEntityManagerFactoryBean universContractEntityManagerFactory) { if (universContractEntityManagerFactory == null) { throw new SyncUnknowException("universContractEntityManagerFactory is null"); } EntityManagerFactory entityManagerFactory = universContractEntityManagerFactory.getObject(); if (entityManagerFactory == null) { throw new SyncUnknowException("entityManagerFactory is null"); } return new JpaTransactionManager(entityManagerFactory); } }
核心问题
需要实现:当业务方法同时操作两个Oracle数据库时,保证所有数据库操作要么全部成功,要么全部回滚。示例业务方法如下:
@Override @Transactional("chainedTransactionManager") public void save(CompanyDTO companyDTO) { log.info("method=save ' - CompanyServiceImpl"); CompanyDTO companyDTOWithLegacyCode = companyDTO.toBuilder().build(); legacyCodeService.changeToLegacyCode(companyDTOWithLegacyCode); // 操作第二个数据库 Company company = companyBuilder.universContractDtoToSisbfEntity(companyDTOWithLegacyCode); companyRepository.save(company); // 操作第一个数据库 legacyCodeService.updateLegacyCode(ClassTypeEnum.COMPANY, companyDTO.getCompanyCode(), company.getCompanyCode()); // 操作第二个数据库 log.info("finish method=save ' - CompanyServiceImpl"); }
解决方案
要实现上述需求,需完成以下三步:
1. 配置第二个数据源的完整上下文
参照第一个数据源的配置,创建第二个数据源(比如LegacyCode)的配置类,确保Repository、EntityManagerFactory、TransactionManager都独立配置:
@Configuration @EnableTransactionManagement @EnableJpaRepositories(basePackages = "br.com.xyz.univers.ms.universcontractsync.repository.legacycode", entityManagerFactoryRef = "legacyCodeEntityManagerFactory", transactionManagerRef= "legacyCodeTransactionManager" ) public class LegacyCodeDatasourceConfiguration { @Bean @ConfigurationProperties("app.datasource.legacycode") public DataSourceProperties legacyCodeDataSourceProperties() { return new DataSourceProperties(); } @Bean @ConfigurationProperties("app.datasource.legacycode.hikari") public DataSource legacyCodeDataSource() { return legacyCodeDataSourceProperties().initializeDataSourceBuilder() .type(HikariDataSource.class).build(); } @Bean(name = "legacyCodeEntityManagerFactory") public LocalContainerEntityManagerFactoryBean legacyCodeEntityManagerFactory( EntityManagerFactoryBuilder builder) { return builder .dataSource(legacyCodeDataSource()) .packages("br.com.xyz.univers.ms.universcontractsync.models.legacycode") .build(); } @Bean(name = "legacyCodeTransactionManager") public PlatformTransactionManager legacyCodeTransactionManager( final @Qualifier("legacyCodeEntityManagerFactory") LocalContainerEntityManagerFactoryBean legacyCodeEntityManagerFactory) { if (legacyCodeEntityManagerFactory == null) { throw new SyncUnknowException("legacyCodeEntityManagerFactory is null"); } EntityManagerFactory entityManagerFactory = legacyCodeEntityManagerFactory.getObject(); if (entityManagerFactory == null) { throw new SyncUnknowException("entityManagerFactory is null"); } return new JpaTransactionManager(entityManagerFactory); } }
2. 配置链式事务管理器(ChainedTransactionManager)
创建一个全局的事务管理器,将两个数据源的事务管理器串联起来,实现多数据源的事务控制:
@Configuration public class ChainedTransactionConfig { @Bean(name = "chainedTransactionManager") public PlatformTransactionManager chainedTransactionManager( @Qualifier("universContractTransactionManager") PlatformTransactionManager universContractTxManager, @Qualifier("legacyCodeTransactionManager") PlatformTransactionManager legacyCodeTxManager) { return new ChainedTransactionManager(universContractTxManager, legacyCodeTxManager); } }
注意:ChainedTransactionManager的执行顺序是按传入的顺序,回滚时会反向逐个回滚。但它属于弱一致性事务——如果最后一个事务提交失败,前面已经提交的事务无法回滚(因为数据库已经持久化)。如果需要强一致性,需使用XA事务(JTA),但XA会带来性能损耗。
3. 确保业务方法的事务配置正确
在业务方法上通过@Transactional("chainedTransactionManager")指定使用链式事务管理器,同时确保被调用的子方法(比如legacyCodeService中的方法)事务传播属性为默认的REQUIRED(即加入当前事务),无需额外修改。
关键注意事项
- 两个数据源的配置必须完全独立,包括Repository扫描路径、EntityManagerFactory、TransactionManager的命名
- 如果需要强一致性事务,需改用XA事务:配置支持XA的数据源(比如OracleXADataSource),并使用JtaTransactionManager
- 链式事务管理器适合对一致性要求不是极端严格、且希望避免XA性能开销的场景
内容的提问来源于stack exchange,提问作者Sérgio M.
相关产品推荐
相关产品推荐

