【Transaction】jta-atomikos + Springboot JPA 多数据源事务回滚

tech2026-08-09  2

依赖

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jta-atomikos</artifactId> </dependency>

配置

# 多数据源配置 spring: datasource: ds1: driverClassName: org.postgresql.Driver jdbcUrl: jdbc:postgresql://xxx/bigdata?currentSchema=public username: xxx password: xxx ds3: driverClassName: org.postgresql.Driver jdbcUrl: jdbc:postgresql://xxx/xxx?currentSchema=public username: xxxx password: xxxx jpa: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect open-in-view: true show-sql: true database-platform: org.hibernate.dialect.PostgreSQLDialect

配置类

@Configuration @ConfigurationProperties(prefix = "spring.datasource.ds2") @Data public class GPProperties { private String jdbcUrl; private String username; private String password; private String driverClassName; } @Configuration @ConfigurationProperties(prefix = "spring.datasource.ds1") @Data public class PGProperties { private String jdbcUrl; private String username; private String password; private String driverClassName; } @Configuration @EnableTransactionManagement @DependsOn("transactionManager") @EnableJpaRepositories( entityManagerFactoryRef = "entityManagerPrimary", transactionManagerRef = "transactionManager", basePackages = {"com.kanq.bigdata.greenplum.dao.ds1"}) //设置Repository所在位置 public class PrimaryConfig { @Autowired private JpaVendorAdapter jpaVendorAdapter; @Autowired @Qualifier("primaryDataSource") private DataSource primaryDataSource; @Bean(name = "entityManagerPrimary") public LocalContainerEntityManagerFactoryBean orderEntityManager() throws Throwable { HashMap<String, Object> properties = new HashMap<String, Object>(); properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName()); properties.put("javax.persistence.transactionType", "JTA"); LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean(); entityManager.setJtaDataSource(primaryDataSource); entityManager.setJpaVendorAdapter(jpaVendorAdapter); entityManager.setPackagesToScan("com.kanq.bigdata.greenplum.entity.pg"); entityManager.setPersistenceUnitName("layerPersistenceUnit"); entityManager.setJpaPropertyMap(properties); return entityManager; } } @Configuration @DependsOn("transactionManager") @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "entityManagerSecond", transactionManagerRef = "transactionManager", basePackages = {"com.kanq.bigdata.greenplum.dao.ds2"}) //设置Repository所在位置 public class SecondConfig { @Autowired private JpaVendorAdapter jpaVendorAdapter; @Autowired @Qualifier("secondDataSource") private DataSource secondDataSource; @Bean(name = "entityManagerSecond") public LocalContainerEntityManagerFactoryBean orderEntityManager() throws Throwable { HashMap<String, Object> properties = new HashMap<String, Object>(); properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName()); properties.put("javax.persistence.transactionType", "JTA"); LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean(); entityManager.setJtaDataSource(secondDataSource); entityManager.setJpaVendorAdapter(jpaVendorAdapter); entityManager.setPackagesToScan("com.kanq.bigdata.greenplum.entity.gp"); entityManager.setPersistenceUnitName("metaPersistenceUnit"); entityManager.setJpaPropertyMap(properties); return entityManager; } } @Configuration @ComponentScan @EnableTransactionManagement public class DataSourceConfig { // @ConfigurationProperties(prefix="spring.datasource.ds1") @Bean(name = "primaryDataSource",initMethod = "init", destroyMethod = "close") @Qualifier("primaryDataSource") @Primary public DataSource primaryDataSource(PGProperties pgProperties) { // DataSource pg = DataSourceBuilder.create().build(); PGXADataSource pgxa=new PGXADataSource(); pgxa.setURL(pgProperties.getJdbcUrl()); pgxa.setUser(pgProperties.getUsername()); pgxa.setPassword(pgProperties.getPassword()); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(pgxa); xaDataSource.setUniqueResourceName("primaryDataSource"); return xaDataSource; } @Bean(name = "secondDataSource") @Qualifier("secondDataSource") public DataSource secondaryDataSource(GPProperties gpProperties) { PGXADataSource gpxa=new PGXADataSource(); gpxa.setURL(gpProperties.getJdbcUrl()); gpxa.setUser(gpProperties.getUsername()); gpxa.setPassword(gpProperties.getPassword()); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(gpxa); xaDataSource.setUniqueResourceName("secondDataSource"); return xaDataSource; } @Bean public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } //设置JPA特性 @Bean public JpaVendorAdapter jpaVendorAdapter() { HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter(); //显示sql hibernateJpaVendorAdapter.setShowSql(true); //自动生成/更新表 hibernateJpaVendorAdapter.setGenerateDdl(false); //设置数据库类型 hibernateJpaVendorAdapter.setDatabase(Database.POSTGRESQL); return hibernateJpaVendorAdapter; } @Bean(name = "atomikosTransactionManager",initMethod = "init", destroyMethod = "close") public TransactionManager atomikosTransactionManager() throws Throwable { UserTransactionManager userTransactionManager = new UserTransactionManager(); userTransactionManager.setForceShutdown(false); AtomikosJtaPlatform.transactionManager = userTransactionManager; return userTransactionManager; } @Bean(name = "userTransaction") public UserTransaction userTransaction() throws Throwable { UserTransactionImp userTransactionImp = new UserTransactionImp(); userTransactionImp.setTransactionTimeout(10000); return userTransactionImp; } @Bean(name = "transactionManager") @DependsOn({"userTransaction", "atomikosTransactionManager"}) public PlatformTransactionManager transactionManager() throws Throwable { UserTransaction userTransaction = userTransaction(); AtomikosJtaPlatform.transaction = userTransaction; TransactionManager atomikosTransactionManager = atomikosTransactionManager(); return new JtaTransactionManager(userTransaction, atomikosTransactionManager); } } import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; public class AtomikosJtaPlatform extends AbstractJtaPlatform { private static final long serialVersionUID = 1L; static TransactionManager transactionManager; static UserTransaction transaction; @Override protected TransactionManager locateTransactionManager() { return transactionManager; } @Override protected UserTransaction locateUserTransaction() { return transaction; } }

Entity、Repository

Entity、Repository都需要分2个文件夹,配置中配置了不同数据源使用不同的文件夹

Controller

需要加事务@Transactional

@RequestMapping(value = "/test", method = RequestMethod.POST) @Transactional public ResultModel test(){ Map<String,Object> res=null; res=service.test(); return Message.success(res); }

Service

如果Controller没有@Transactional注解,这里回滚会出错,提示没有事务。

//atomikos事务 @Transactional public boolean test(){ try { XXX xxx=new XXX("wo",1); YYY yyy=new YYY("qaq",2); xxxDao.saveAndFlush(xxx); yyyDao.saveAndFlush(yyy); }catch (Exception e){ log.error("数据更新异常",e); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return false; } return true; }

2个不同数据源的事务都会回滚。

最新回复(0)