javaspringhibernatespring-bootatomikos

JTA, Hibernate with atomikos Multiple XA Datasources not maintaing transaction in SpringBoot


I have 2 MySQL schema and respective MysqlXADataSource configured as below -

@Bean(name = "sourceDataSource")
@Primary
public DataSource dataSource() {
    MysqlXADataSource dataSource = new MysqlXADataSource();
    dataSource.setPinGlobalTxToPhysicalConnection(true);
    dataSource.setUrl(DB_URL);
    dataSource.setUser(DB_USERNAME);
    dataSource.setPassword(DB_PASSWORD);

    AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setUniqueResourceName("SourceDB");
    atomikosDataSourceBean.setXaDataSource(dataSource);

    /*DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(DB_DRIVER);
    dataSource.setUrl(DB_URL);
    dataSource.setUsername(DB_USERNAME);
    dataSource.setPassword(DB_PASSWORD);*/
    System.out.println("Creatign Source data source.");
    return atomikosDataSourceBean;
}

@Bean(name = "sourceSessionFactory")
public LocalSessionFactoryBean sessionFactory(@Qualifier("jtaTransactionManager") JtaTransactionManager jts) {
    LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
    sessionFactoryBean.setDataSource(dataSource());
    sessionFactoryBean.setPackagesToScan(ENTITYMANAGER_PACKAGES_TO_SCAN);
    Properties hibernateProperties = new Properties();
    hibernateProperties.put("hibernate.dialect", HIBERNATE_DIALECT);
    hibernateProperties.put("hibernate.show_sql", HIBERNATE_SHOW_SQL);
    hibernateProperties.put("hibernate.hbm2ddl.auto", HIBERNATE_HBM2DDL_AUTO);
    hibernateProperties.put("hibernate.transaction.factory_class", "org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory");
    hibernateProperties.put("hibernate.transaction.coordinator_class", "jta");
    hibernateProperties.put("hibernate.transaction.jta.platform", "com.atomikos.icatch.jta.hibernate4.AtomikosPlatform");

    //hibernateProperties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
    sessionFactoryBean.setJtaTransactionManager(jts);
    sessionFactoryBean.setHibernateProperties(hibernateProperties);
    System.out.println("Creatign Source Session Factory.");
    return sessionFactoryBean;
}

And

@Bean(name="destinationDataSource")
public DataSource dataSource() {
    MysqlXADataSource dataSource = new MysqlXADataSource();
    dataSource.setPinGlobalTxToPhysicalConnection(true);
    dataSource.setUrl(DB_URL);
    dataSource.setUser(DB_USERNAME);
    dataSource.setPassword(DB_PASSWORD);

    AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setUniqueResourceName("DestinationDB");
    atomikosDataSourceBean.setXaDataSource(dataSource);

    /*DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(DB_DRIVER);
    dataSource.setUrl(DB_URL);
    dataSource.setUsername(DB_USERNAME);
    dataSource.setPassword(DB_PASSWORD);

    System.out.println("Creatign Destination Data Source.");*/
    return atomikosDataSourceBean;
}

@Bean(name="destinationSessionFactory")
public LocalSessionFactoryBean sessionFactory(@Qualifier("jtaTransactionManager") JtaTransactionManager jts) {
    LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
    sessionFactoryBean.setDataSource(dataSource());
    sessionFactoryBean.setPackagesToScan(ENTITYMANAGER_PACKAGES_TO_SCAN);
    Properties hibernateProperties = new Properties();
    hibernateProperties.put("hibernate.dialect", HIBERNATE_DIALECT);
    hibernateProperties.put("hibernate.show_sql", HIBERNATE_SHOW_SQL);
    hibernateProperties.put("hibernate.hbm2ddl.auto", HIBERNATE_HBM2DDL_AUTO);
    hibernateProperties.put("hibernate.transaction.factory_class", "org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory");
    hibernateProperties.put("hibernate.transaction.coordinator_class", "jta");
    hibernateProperties.put("hibernate.transaction.jta.platform", "com.atomikos.icatch.jta.hibernate4.AtomikosPlatform");
    sessionFactoryBean.setJtaTransactionManager(jts);
    sessionFactoryBean.setHibernateProperties(hibernateProperties);
    System.out.println("Creatign Destination Session Factoy.");
    return sessionFactoryBean;
}

Transaction Configuration

@Bean("transactionManager")
public UserTransactionManager transactionManager(){
    UserTransactionManager userTransactionManager = new UserTransactionManager();
    return userTransactionManager;
}

@Bean("userTransaction")
public UserTransaction userTransaction(){
    J2eeUserTransaction userTransaction = new J2eeUserTransaction();
    return userTransaction;
}

@Bean("jtaTransactionManager")
public JtaTransactionManager jtaTransactionManager(@Qualifier("transactionManager") UserTransactionManager userTransactionManager, @Qualifier("userTransaction") UserTransaction userTransaction) {
    JtaTransactionManager transactionManager = new JtaTransactionManager();
    transactionManager.setTransactionManager(userTransactionManager);
    transactionManager.setUserTransaction(userTransaction);
    //transactionManager.setSessionFactory(sessionFactory().getObject());
    System.out.println("Creatign JTA Transaction Manager.");
    return transactionManager;
}

Service ...

@Transactional(transactionManager = "jtaTransactionManager", propagation=Propagation.REQUIRES_NEW, rollbackFor=Exception.class, timeout=500000)
public void transform() throws JobExecutionException{
    System.out.println("START == Starting Trandformation ... ");

    //Clean Up
    transformProduct.cleanUp();
    transformService.cleanUp();
    transformResources.cleanUp();

    transformResources.transform();
    transformService.transform();
    transformProduct.transform();
    LOG.info("END == Starting Transformation ... ");
    throw new JobExecutionException();
}

And there are respective sub service classes transformProduct, transformService & transformResources and these classes are using the sessionFactory implemented dao classes. I have no where mentioned commit or rollback. everything should be transnational. but somehow its not working


Solution

  • Ok.. There was mistake in the code -

    1. XA Datasource must be wrapped with AtomikosDataSourceBean ans should return AtomikosDataSourceBean reference.
    2. Assign unique name to AtomikosDataSourceBean.
    3. Service Method should be made @Transactional(transactionManager = "jtaTransactionManager" ...), provide Spring JtaTransactionManager.
    4. JtaTransactionManager should be referenced with javax.transaction.UserTransaction having com.atomikos.icatch.jta.J2eeUserTransaction implementation.
    5. JtaTransactionManager should be referenced with com.atomikos.icatch.jta.UserTransactionManager implementation.
    6. Rest of the Code is correct in the question.

    This is straight forward out-of-the-box support for Atomikos.