How do I set up a JtaTransactionManager transaction manager to do distributed transactions in different databases? Below I quote a complete configuration that a) doesn’t save a record in the database because a transaction manager is not working b) check if rollback works if I cannot insert into one of the databases, because item a does not work.

Exception is not caught, the catch code does not fall.

I suspect that the transaction manager must somehow transfer the dataSources with which it will work.

Configuration:

package sp.config; import com.atomikos.icatch.jta.J2eeUserTransaction; import com.atomikos.icatch.jta.UserTransactionImp; import com.atomikos.icatch.jta.UserTransactionManager; import com.atomikos.jdbc.AtomikosDataSourceBean; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.Database; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.jta.JtaTransactionManager; import javax.sql.DataSource; @Configuration public class NewDbConfig { @Bean(name="dataSourceA") public AtomikosDataSourceBean dataSourceA() { AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean(); dataSource.setUniqueResourceName("DataSourceA"); MysqlXADataSource mysqlXADataSource = new MysqlXADataSource(); mysqlXADataSource.setDatabaseName("atomikos_one"); mysqlXADataSource.setServerName("localhost"); mysqlXADataSource.setPort(3306); mysqlXADataSource.setUser("root"); mysqlXADataSource.setPassword("mypass"); mysqlXADataSource.setUrl("jdbc:mysql://localhost:3306/atomikos_one"); dataSource.setXaDataSource(mysqlXADataSource); return dataSource; } @Bean(name="dataSourceB") public AtomikosDataSourceBean dataSourceB() { AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean(); dataSource.setUniqueResourceName("DataSourceB"); MysqlXADataSource mysqlXADataSource = new MysqlXADataSource(); mysqlXADataSource.setDatabaseName("atomikos_two"); mysqlXADataSource.setServerName("localhost"); mysqlXADataSource.setPort(3306); mysqlXADataSource.setUser("root"); mysqlXADataSource.setPassword("mypass"); mysqlXADataSource.setUrl("jdbc:mysql://localhost:3306/atomikos_two"); dataSource.setXaDataSource(mysqlXADataSource); return dataSource; } @Bean(name="entityManagerFactoryA") public LocalContainerEntityManagerFactoryBean entityManagerFactoryA( @Qualifier("dataSourceA") DataSource dataSource, @Qualifier("vendorAdapterA") JpaVendorAdapter vendorAdapter ) { LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); factoryBean.setPackagesToScan("sp.model.atomikosA"); factoryBean.setDataSource(dataSource); factoryBean.setJpaVendorAdapter(vendorAdapter); return factoryBean; } @Bean(name="vendorAdapterA") public JpaVendorAdapter hibernateJpaVendorAdapterA() { HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setShowSql(true); vendorAdapter.setDatabase(Database.MYSQL); vendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQL5Dialect"); return vendorAdapter; } @Bean(name="entityManagerFactoryB") public LocalContainerEntityManagerFactoryBean entityManagerFactoryB( @Qualifier("dataSourceB") DataSource dataSource, @Qualifier("vendorAdapterB") JpaVendorAdapter vendorAdapter ) { LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); factoryBean.setPackagesToScan("sp.model.atomikosB"); factoryBean.setDataSource(dataSource); factoryBean.setJpaVendorAdapter(vendorAdapter); return factoryBean; } @Bean(name="vendorAdapterB") public JpaVendorAdapter hibernateJpaVendorAdapterB() { HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setShowSql(true); vendorAdapter.setDatabase(Database.MYSQL); vendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQL5Dialect"); return vendorAdapter; } @Bean(name="atomikosTransactionManager") public UserTransactionManager userTransactionManager() { UserTransactionManager transactionManager = new UserTransactionManager(); transactionManager.setForceShutdown(false); return transactionManager; } @Bean(name="atomikosUserTransaction") public UserTransactionImp atomikosUserTransaction() { UserTransactionImp j2eeUserTransaction = new UserTransactionImp(); try { j2eeUserTransaction.setTransactionTimeout(300); } catch (Exception e) { } return j2eeUserTransaction; } @Bean(name="transactionManager") public JtaTransactionManager jtaTransactionManager( @Qualifier("atomikosTransactionManager") UserTransactionManager userTransactionManager, @Qualifier("atomikosUserTransaction") UserTransactionImp j2eeUserTransaction ) { JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(); jtaTransactionManager.setTransactionManager(userTransactionManager); jtaTransactionManager.setUserTransaction(j2eeUserTransaction); jtaTransactionManager.setAllowCustomIsolationLevels(true); return jtaTransactionManager; } } 

Dao 1:

 package sp.dao.atomikosA; import sp.model.atomikosA.Customer; import org.springframework.stereotype.Service; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Service public class CustomerADaoImpl implements CustomerADao { @PersistenceContext(unitName="entityManagerFactoryA") protected EntityManager em; public void persistItem(Customer customer) { em.persist(customer); } } 

Dao 2:

 package sp.dao.atomikosB; import sp.model.atomikosB.Order; import org.springframework.stereotype.Service; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Service public class OrderBDaoImpl implements OrderBDao { @PersistenceContext(unitName="entityManagerFactoryB") protected EntityManager em; public void persistItem(Order order) { em.persist(order); } } 

Service:

 package sp.services; import sp.dao.atomikosA.CustomerADao; import sp.dao.atomikosB.OrderBDao; import sp.model.atomikosA.Customer; import sp.model.atomikosB.Order; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service("employeeService") public class EmployeeServiceImpl implements EmployeeService { @Autowired private CustomerADao customerADao; @Autowired private OrderBDao orderBDao; @Transactional(rollbackFor=Exception.class) public void persistEmployees(Customer employeeA, Order employeeB) throws Exception { System.out.println("Persist A"); customerADao.persistItem(employeeA); System.out.println("Persist A OK - persist B"); orderBDao.persistItem(employeeB); System.out.println("Persist B okk"); } @Transactional public void persistCustomer(Customer customer) throws Exception { System.out.println("Persist A"); customerADao.persistItem(customer); } } 

And the actual service call from the controller:

 @RequestMapping("/") @ResponseBody public String index() { sp.model.atomikosA.Customer customer = new sp.model.atomikosA.Customer(); customer.setName("Maksat"); customer.setAge(27); customer.setCustId(3L); Order order = new Order(); order.setCustId(3L); order.setCode(1); order.setQuantity(1); try { employeeService.persistEmployees(customer, order); employeeService.persistCustomer(customer); } catch (Exception e) { int del = 2; } return "success"; } 

UPD:

When I created a simple JpaTransactionManager:

 @Bean public PlatformTransactionManager transactionManager(EntityManagerFactory emf){ JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(emf); return transactionManager; } 

Then in the emf bin argument I previously set the dataSource:

 em.setDataSource(dataSource); 

So my PlatformTransactionManager knew the dataSource that it manages.

And when creating BitronixConfig, we don’t give the transaction manager any information about the dataSource that it will manage. How then will he understand what bases he has in control?

 @Bean public PlatformTransactionManager bitronixTransactionManager() { JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(); jtaTransactionManager.setTransactionManager(TransactionManagerServices.getTransactionManager()); jtaTransactionManager.setUserTransaction(TransactionManagerServices.getTransactionManager()); jtaTransactionManager.setAllowCustomIsolationLevels(true); jtaTransactionManager.setNestedTransactionAllowed(true); jtaTransactionManager.afterPropertiesSet(); return jtaTransactionManager; } 

UPD 2:

Made by analogy with https://github.com/Cepr0/dual-db-demo . I started a transaction within one table, the first value is valid, the second is not valid. With the transaction should not be recorded anything. But the first record was added, but the second one was not The transaction did not work within the same database. Within two databases, a distributed transaction does not work either. What did I do in the config wrong?

 @Configuration public class BitronixConfig { @Bean public PlatformTransactionManager bitronixTransactionManager() { JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(); jtaTransactionManager.setTransactionManager(TransactionManagerServices.getTransactionManager()); jtaTransactionManager.setUserTransaction(TransactionManagerServices.getTransactionManager()); jtaTransactionManager.setAllowCustomIsolationLevels(true); jtaTransactionManager.setNestedTransactionAllowed(true); jtaTransactionManager.afterPropertiesSet(); return jtaTransactionManager; } } @Configuration @EnableTransactionManagement @EnableJpaRepositories(value = "sp.first.repo", entityManagerFactoryRef = "firstEntityManagerFactory", transactionManagerRef = "bitronixTransactionManager") public class FirstDataSourceConfig { @Primary @Bean(name = "firstDataSource") public DataSource firstDataSource() { PoolingDataSource pds = new PoolingDataSource(); pds.setUniqueName("first"); pds.setClassName("com.mysql.cj.jdbc.MysqlXADataSource"); pds.setMaxPoolSize(16); pds.setMinPoolSize(5); pds.setAllowLocalTransactions(true); pds.setIsolationLevel("READ_COMMITTED"); pds.setShareTransactionConnections(true); pds.setEnableJdbc4ConnectionTest(true); Properties driverProperties = pds.getDriverProperties(); driverProperties.put("user", "root"); driverProperties.put("password", "mypass"); driverProperties.put("url", "jdbc:mysql://localhost:3306/atomikos_one"); pds.init(); return pds; } @Primary @Bean(name = "firstEntityManagerFactory") @Autowired public LocalContainerEntityManagerFactoryBean entityManagerFactory( @Qualifier("firstDataSource") DataSource dataSource, @Qualifier("vendorAdapter") JpaVendorAdapter vendorAdapter ) { Map<String, String> propertiesNew = new HashMap<>(); propertiesNew.put("hibernate.hbm2ddl.auto", "update"); LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean(); bean.setJtaDataSource(dataSource); bean.setPackagesToScan("sp.first.model"); bean.setPersistenceUnitName("first"); bean.setJpaPropertyMap(propertiesNew); bean.setJpaVendorAdapter(vendorAdapter); return bean; } @Bean(name="vendorAdapter") public JpaVendorAdapter hibernateJpaVendorAdapter() { HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setShowSql(true); vendorAdapter.setDatabase(Database.MYSQL); vendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQL5Dialect"); return vendorAdapter; } @Bean(name="properties") public Map<String, String> properties() { Map<String, String> properties = new HashMap<>(); properties.put("hibernate.format_sql", "true"); properties.put("hibernate.transaction.jta.platform", "org.hibernate.engine.transaction.jta.platform.internal.BitronixJtaPlatform"); properties.put("hibernate.transaction.jta.enable", "true"); properties.put("hibernate.connection.release_mode", "after_statement"); return properties; } } @Configuration @EnableTransactionManagement @EnableJpaRepositories(value = "sp.second.repo", entityManagerFactoryRef = "secondEntityManagerFactory", transactionManagerRef = "bitronixTransactionManager") public class SecondDataSourceConfig { @Bean(name = "secondDataSource") public DataSource secondDataSource() { PoolingDataSource pds = new PoolingDataSource(); pds.setUniqueName("second"); pds.setClassName("com.mysql.cj.jdbc.MysqlXADataSource"); pds.setMaxPoolSize(16); pds.setMinPoolSize(5); pds.setAllowLocalTransactions(true); pds.setIsolationLevel("READ_UNCOMMITTED"); pds.setShareTransactionConnections(true); pds.setEnableJdbc4ConnectionTest(true); Properties driverProperties = pds.getDriverProperties(); driverProperties.put("user", "root"); driverProperties.put("password", "mypass"); driverProperties.put("url", "jdbc:mysql://localhost:3306/atomikos_two"); pds.init(); return pds; } @Bean(name = "secondEntityManagerFactory") public LocalContainerEntityManagerFactoryBean entityManagerFactory( @Qualifier("secondDataSource") DataSource dataSource, @Qualifier("vendorAdapter") JpaVendorAdapter vendorAdapter ) { Map<String, String> propertiesNew = new HashMap<>(); propertiesNew.put("hibernate.hbm2ddl.auto", "update"); LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean(); bean.setJtaDataSource(dataSource); bean.setPackagesToScan("sp.second.model"); bean.setPersistenceUnitName("second"); bean.setJpaPropertyMap(propertiesNew); bean.setJpaVendorAdapter(vendorAdapter); return bean; } } 
  • Why is the transaction manager not working? Give an example in this case. - Roman C
  • 1) you have the SPring boot - and this means that the configuration of the datasource needs to be transferred to the * .properties resource file 2) Leave the EntityManager for implementations Repository (Jpa / Crud / etc) i.e. Throw it out completely - the code is smaller and more convenient, but it loses in performance with standard Query JDBC 3) If you display data in the view layer, override the implementation as an OpenInViewEntityManager bin and in 99% of cases you can forget about Transactional annotation - GenCloud
  • EmployeeServiceImpl: persistEmployees () and EmployeeServiceImpl: persistCustomer () - work, but in fact the insert database does not occur - Maksat Orunkhanov
  • Look at my repo github.com/Cepr0/dual-db-demo , suddenly help. There, though Bitroniks, not Atomicos, but nevertheless ... (pay attention - in the main pom.xml two profiles - for postgres and muscular) - Cepr0
  • @ Cepr0 updated the information in question UPD - Maksat Orunkhanov

0