如何使用 Spring JTA 将两个数据源配置为单个事务管理器,以便在异常时回滚两个数据源

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

我想配置两个数据源,一个是 Oracle,一个是 My-SQL 到 Spring JTA 中的事务管理器来执行 2 阶段提交,以便任何一个事务任何数据源失败,spring JTA 需要对两个数据源执行回滚

*听说atomikos可以用来配置这种场景 请提供一些配置代码是任何对实现此代码有用的东西 *

Git url 整个代码: 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; 
} 
}
}

下面是上述问题的工作代码:

用于通过开箱即用集成将两个数据源连接到单个事务管理器的 Atomikos 事务管理器的整个项目将在下面提供 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();
    }

}
}

您可以查看我的 github link 以获取两阶段 (2PC) 协议演示的完整示例 Web 应用程序,该演示首先将数据存储在 MySQl 数据库中,然后再存储在 Oracle 数据库中,然后如果 oracle 数据库抛出异常然后回滚也发生在 MySql 数据库

中,也保持 ACID

Git hub link for 2PC Sample Web application Two Phase (2PC) demo application

我没有使用第 3 方 JTA 提供程序,而是使用 J2EE 兼容应用程序服务器 JBOSS AS 7 用于 JTA 服务。