【问题标题】: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如何使用 Spring JTA 将两个数据源配置为单个事务管理器,以便在异常情况下两个数据源都回滚
【发布时间】:2017-10-13 07:44:25
【问题描述】:

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

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

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

【问题讨论】:

    标签: spring datasource spring-transactions jta transactionmanager


    【解决方案1】:

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

    通过开箱即用的集成将两个数据源连接到单个事务管理器的 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();
        }
    
    }
    }
    

    【讨论】:

      【解决方案2】:

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

      2PC 示例 Web 应用程序的 Git 中心链接 Two Phase (2PC) demo application

      我没有使用第 3 方 JTA 提供程序,而是使用符合 J2EE 标准的应用程序服务器 JBOSS AS 7 来提供 JTA 服务。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-12-20
        • 2011-07-11
        • 2018-02-26
        • 2021-06-22
        • 1970-01-01
        相关资源
        最近更新 更多