springdatasourcespring-transactionsjtatransactionmanager

How to configure Two Data Sources to single Transaction manager using Spring JTA,So that on the exception Both of the data sources get rolled back


I would like to configure the two Data sources one is Oracle and one is My-SQL to the Transaction Manager in the Spring JTA to perform the 2 phase commit so that any one transaction of the any of the Data sources get failed and the spring JTA needs to perform the roll back on the both of the data sources

*Heard the atomikos can we used to configure this kind of scenarios Please provide the some configuration codes are anything that would be useful in implementing this code *

Git url for the entire code: https://github.com/mageshsrinivasulu/poc

spring-beans.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">


 <bean class="org.springframework.beans.factory.config
.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:Application.properties" />
</bean>

<bean id="oracleSessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="oracleDataSource" />
    <property name="mappingResources">
        <list>
            <value>persons.hbm.xml </value>
        </list>
    </property>

    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
            <prop key="hibernate.autocommit">false</prop>
            <!-- <prop key="hibernate.current_session_context_class">thread</prop> -->
            <prop key="hibernate.c3p0.min_size">5</prop>
            <prop key="hibernate.c3p0.max_size">10</prop>
            <prop key="hibernate.c3p0.timeout">300</prop>
        </props>
    </property>

</bean>

<bean id="mysqlSessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="mysqlDataSource" />
    <property name="mappingResources">
        <list>
            <value>persons.hbm.xml </value>
        </list>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="hibernate.autocommit">false</prop>
            <!-- <prop key="hibernate.current_session_context_class">thread</prop> -->
            <prop key="hibernate.c3p0.min_size">5</prop>
            <prop key="hibernate.c3p0.max_size">10</prop>
            <prop key="hibernate.c3p0.timeout">300</prop>
        </props>
    </property>
</bean>
<!-- Creating TransactionManager Bean, since JDBC we are creating of type 
    DataSourceTransactionManager -->



<bean id="oracleTransactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="oracleDataSource" />

</bean>

<bean id="mysqlTransactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="mysqlDataSource" />
</bean>


<bean id="oracleDataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${oracle.hibernate.connection.driver_class}" />
    <property name="url" value="${oracle.hibernate.connection.url}" />
    <property name="username" value="${oracle.Username}" />
    <property name="password" value="${oracle.Password}" />

</bean>

<bean id="mysqlDataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${mysql.jdbc.driverClassName}" />
    <property name="url" value="${mysql.hibernate.connection.url}" />
    <property name="username" value="${mysql.Username}" />
    <property name="password" value="${mysql.Password}" />

</bean>

<bean id="persons" class="com.bnym.aal.poc.spring_jta.Persons">
</bean>

<bean id="App" class="com.bnym.aal.poc.spring_jta.App">
    <property name="springJtaDaoClass" ref="springJtaDaoClass" />
</bean>

<bean id="springJtaDaoClass" class="com.bnym.aal.poc.spring_jta.springJtaDaoClass">
    <property name="oracleSessionFactory" ref="oracleSessionFactory" />
    <property name="mysqlSessionFactory" ref="mysqlSessionFactory" />
    <property name="oracleTransactionManager" ref="oracleTransactionManager" />
    <property name="mysqlTransactionManager" ref="mysqlTransactionManager" />
    <property name="persons" ref="persons" />
</bean>

springJtaDaoClass

import java.io.Serializable;
import java.util.List;
import javax.sql.DataSource;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.hibernate4.HibernateTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
package com.bnym.aal.poc.spring_jta;

public class springJtaDaoClass implements Serializable
{
/**
*
*/
private static final long serialVersionUID = 1L;
private static SessionFactory oracleSessionFactory;
private static SessionFactory mysqlSessionFactory;
private HibernateTemplate oracleJdbcTemplateObject;
private HibernateTemplate mysqlJdbcTemplateObject;
private static Persons persons;

public static SessionFactory getOracleSessionFactory() {
return oracleSessionFactory;
}
public void setOracleSessionFactory(SessionFactory oracleSessionFactory) {
springJtaDaoClass.oracleSessionFactory = oracleSessionFactory;
this.oracleJdbcTemplateObject=new HibernateTemplate(oracleSessionFactory);
}
public static SessionFactory getMysqlSessionFactory() {
return mysqlSessionFactory;
}
public void setMysqlSessionFactory(SessionFactory mysqlSessionFactory) {
springJtaDaoClass.mysqlSessionFactory = mysqlSessionFactory;
this.mysqlJdbcTemplateObject=new HibernateTemplate(mysqlSessionFactory);
}

public static PlatformTransactionManager oracleTransactionManager;
public static PlatformTransactionManager mysqlTransactionManager;

public static PlatformTransactionManager getOracleTransactionManager() {
return oracleTransactionManager;
}
public static void setOracleTransactionManager(PlatformTransactionManager 
oracleTransactionManager) {
springJtaDaoClass.oracleTransactionManager = oracleTransactionManager;
}
public static PlatformTransactionManager getMysqlTransactionManager() {
return mysqlTransactionManager;
}
public static void setMysqlTransactionManager(PlatformTransactionManager 
mysqlTransactionManager) {
springJtaDaoClass.mysqlTransactionManager = mysqlTransactionManager;
}
public static Persons getPersons() {
return persons;
}
public static void setPersons(Persons persons) {
springJtaDaoClass.persons = persons;
}

public void dbOracleAccess()
{
TransactionDefinition oracledef= new DefaultTransactionDefinition();
TransactionStatus oraclestatus = 
oracleTransactionManager.getTransaction(oracledef);

try
{
Persons person1=getPersons().persons(1,"a","b","c","d");
oracleJdbcTemplateObject.save(person1);
oracleTransactionManager.commit(oraclestatus);
}
catch (DataAccessException e) {
System.out.println("Error in creating record, rolling back");
oracleTransactionManager.rollback(oraclestatus);
throw e;
}

}
public void dbMySqlAccess()
{
TransactionDefinition mysqldef= new DefaultTransactionDefinition();
TransactionStatus 
mysqlstatus=mysqlTransactionManager.getTransaction(mysqldef);

try
{
Persons person2=getPersons().persons(2,"e","b","c","d");
mysqlJdbcTemplateObject.save(person2);
mysqlTransactionManager.commit(mysqlstatus);
}
catch (DataAccessException e) {
System.out.println("Error in creating record, rolling back");
mysqlTransactionManager.rollback(mysqlstatus);
throw e; 
} 
}
}

Solution

  • Below is the working code of the above stated issue:

    Entire project for the Atomikos Transaction manager for the connecting the two data sources to the single transaction manager by out of box integration would be available in the below GIT url

    https://github.com/mageshsrinivasulu/spring-jta.git

    spring-beans.xml

    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
    
    <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:Application.properties" />
    </bean>
    
    <bean id="oracleSessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="oraclesDataSource" />
        <property name="mappingResources">
            <list>
                <value>persons.hbm.xml </value>
            </list>
        </property>
    
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
                <!-- <prop key="hibernate.autocommit">true</prop> <prop key="hibernate.connection.isolation">3</prop> -->
                <prop key="hibernate.current_session_context_class">jta</prop>
                <prop key="hibernate.transaction.factory_class">
                    org.hibernate.transaction.CMTTransactionFactory
                </prop>
                <prop key="hibernate.transaction.manager_lookup_class">
                    com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
                </prop>
                <!-- <prop key="hibernate.current_session_context_class">thread</prop> -->
                <prop key="hibernate.c3p0.min_size">5</prop>
                <prop key="hibernate.c3p0.max_size">10</prop>
                <prop key="hibernate.c3p0.timeout">300</prop>
            </props>
        </property>
    
    </bean>
    
    <bean id="mysqlSessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="mysqlDataSource" />
        <property name="mappingResources">
            <list>
                <value>persons.hbm.xml </value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <!-- <prop key="hibernate.autocommit">ture</prop> <prop key="hibernate.connection.isolation">3</prop> -->
                <prop key="hibernate.current_session_context_class">jta</prop>
                <prop key="hibernate.transaction.factory_class">
                    org.hibernate.transaction.CMTTransactionFactory
                </prop>
                <prop key="hibernate.transaction.manager_lookup_class">
                    com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
                </prop>
    
                <!-- <prop key="hibernate.current_session_context_class">thread</prop> -->
                <prop key="hibernate.c3p0.min_size">5</prop>
                <prop key="hibernate.c3p0.max_size">10</prop>
                <prop key="hibernate.c3p0.timeout">300</prop>
            </props>
        </property>
    
    </bean>
    
    
    
    <tx:annotation-driven proxy-target-class="true" />
    
    <tx:jta-transaction-manager
        transaction-manager="atomikosTransactionManager" />
    
    <tx:annotation-driven transaction-manager="atomikosTransactionManager"
        proxy-target-class="true" />
    
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
        init-method="init" destroy-method="close">
        <property name="forceShutdown" value="false" />
    
    </bean>
    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.J2eeUserTransaction">
        <property name="transactionTimeout" value="300" />
    
    </bean>
    
    
    <tx:annotation-driven />
    <bean id="transactionManager"
        class="org.springframework.transaction.jta.JtaTransactionManager"
        depends-on="atomikosTransactionManager,atomikosUserTransaction">
        <property name="transactionManager" ref="atomikosTransactionManager" />
        <property name="userTransaction" ref="atomikosUserTransaction" />
        <property name="allowCustomIsolationLevels" value="true" />
    
    </bean>
    
    <bean id="mysqlDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
        init-method="init" destroy-method="close">
        <property name="uniqueResourceName">
            <value>mySqlDataSource</value>
        </property>
        <property name="xaDataSourceClassName">
            <value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
        </property>
        <property name="xaProperties">
            <props>
                <prop key="databaseName">sys</prop>
                <prop key="serverName">localhost</prop>
                <prop key="port">3306</prop>
                <prop key="user">root</prop>
                <prop key="password">magesh123</prop>
                <prop key="url">jdbc:mysql://localhost:3306/sys</prop>
            </props>
        </property>
        <property name="minPoolSize">
            <value>1</value>
        </property>
    </bean>
    
    
    <bean id="oraclesDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
        init-method="init" destroy-method="close">
        <property name="uniqueResourceName">
            <value>OracleDataSource</value>
        </property>
        <property name="xaDataSourceClassName">
            <value>oracle.jdbc.xa.client.OracleXADataSource</value>
        </property>
        <property name="xaProperties">
            <props>
                <prop key="databaseName">XE</prop>
                <prop key="serverName">localhost</prop>
                <!-- <prop key="port">1521</prop> -->
                <prop key="user">system</prop>
                <prop key="password">magesh123</prop>
                <prop key="URL">jdbc:oracle:thin:@localhost:1521:XE</prop>
            </props>
        </property>
        <property name="minPoolSize">
            <value>1</value>
        </property>
    </bean>
    
    
    
    <bean id="persons" class="com.bnym.aal.poc.spring_jta.Persons">
    </bean>
    
    <bean id="App" class="com.bnym.aal.poc.spring_jta.App">
        <property name="springJtaDaoClass" ref="springJtaDaoClass" />
    </bean>
    
    <bean id="springJtaDaoClass" class="com.bnym.aal.poc.spring_jta.springJtaDaoClass">
        <property name="oracleSessionFactory" ref="oracleSessionFactory" />
        <property name="mysqlSessionFactory" ref="mysqlSessionFactory" />
        <property name="atomikosTransactionManager" ref="transactionManager" />
        <property name="persons" ref="persons" />
    
    </bean>
    

    springJtaDaoClass.java

    package com.bnym.aal.poc.spring_jta;
    
    import java.io.Serializable;
    import org.hibernate.FlushMode;
    import org.hibernate.Hibernate;
    import org.hibernate.HibernateException;
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.springframework.dao.DataAccessException;
    import org.springframework.orm.hibernate4.HibernateTemplate;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.TransactionDefinition;
    import org.springframework.transaction.TransactionStatus;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    import org.springframework.transaction.support.DefaultTransactionDefinition;
    
    public class springJtaDaoClass implements Serializable {
    /**
         * 
         */
    private static final long serialVersionUID = 1L;
    private static SessionFactory oracleSessionFactory;
    private static SessionFactory mysqlSessionFactory;
    
    private HibernateTemplate oracleJdbcTemplateObject;
    private HibernateTemplate mysqlJdbcTemplateObject;
    
    private static Persons persons;
    
    public static SessionFactory getOracleSessionFactory() {
        return oracleSessionFactory;
    }
    
    public void setOracleSessionFactory(SessionFactory oracleSessionFactory) {
        springJtaDaoClass.oracleSessionFactory = oracleSessionFactory;
        this.oracleJdbcTemplateObject = new 
    HibernateTemplate(oracleSessionFactory);
    }
    
    public static SessionFactory getMysqlSessionFactory() {
        return mysqlSessionFactory;
    }
    
     public void setMysqlSessionFactory(SessionFactory mysqlSessionFactory) {
        springJtaDaoClass.mysqlSessionFactory = mysqlSessionFactory;
        this.mysqlJdbcTemplateObject = new 
       HibernateTemplate(mysqlSessionFactory);
    }
    
    public static PlatformTransactionManager atomikosTransactionManager;
    
    public static PlatformTransactionManager getAtomikosTransactionManager() {
        return atomikosTransactionManager;
    }
    
    public static void setAtomikosTransactionManager(PlatformTransactionManager 
        atomikosTransactionManager) {
        springJtaDaoClass.atomikosTransactionManager = 
        atomikosTransactionManager;
    }
    
    public static Persons getPersons() {
        return persons;
    }
    
    public static void setPersons(Persons persons) {
        springJtaDaoClass.persons = persons;
    }
    
    @Transactional()
    public void daoWrapper() throws Exception {
        atomikosTransactionManager = getAtomikosTransactionManager();
        TransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = 
        atomikosTransactionManager.getTransaction(def);
        Persons person1 = new Persons();
        person1.persons(2, "a", "b", "c", "d");
        try {
            getMysqlSessionFactory().getCurrentSession().save(person1);
            getOracleSessionFactory().getCurrentSession().save(person1);
            atomikosTransactionManager.commit(status);
        } catch (Exception e) {
            System.out.println("Error in creating record, rolling back");
            atomikosTransactionManager.rollback(status);
            e.printStackTrace();
        }
    
    }
    }