欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JTA分布式事务实践

程序员文章站 2022-07-16 18:34:19
...

最近一直在研究怎么实现分布式事务,花了不少时间,测试工程启停测试了无数次,最终实现的时候其实也就是写一些配置文件,对于工程代码没什么影响。目前研究还不是很深入,对于全面崩溃恢复如何实现和测试还不清楚。本文先介绍基础的实现。

 

当业务需要在一个事务中操作多个不同的资源,例如多个数据库,消息队列,缓存等,那么就需要使用分布式事务了。在java中一般建议使用JTA,这样开发人员就不用关心什么叫XA协议,什么是两阶段提交协议。要使用JTA需要容器的支持,例如使用JBOSS,WebSphere;或者使用第三方组件例如JOTMAtomikos

 

JBOSS AS现在改名叫Wildfly了,以便和JBOSS EAP区分,后文我也改叫WildflyJOTM看起来是个死项目,我不打算使用。

 

由于目前开发框架基于Spring+JPA设计,所以本文的配置主要是在spring中。其实用EJB的话配置更简单,但需要容器支持。

 

一、Wildfly中配置JTA

1、配置数据库驱动(oracle

a)         首先按照以下路径新增目录:

wildfly-9.0.0.CR1\modules\system\layers\base\com\oracle\ojdbc14\main

b)         把驱动文件ojdbc14.jar复制到此目录下

c)         main目录下新增配置文件module.xml

mudule.xml

<?xml version="1.0" encoding="UTF-8"?>

<module xmlns="urn:jboss:module:1.1" name="com.oracle.ojdbc14">

    <resources>

        <resource-root path="ojdbc14.jar"/>

    </resources>

    <dependencies>

        <module name="javax.api"/>

        <module name="javax.transaction.api"/>

    </dependencies>

</module>

 

打开wildflystandalone.xml配置文件(使用standalone模式启动,domain模式还没尝试),找到<subsystem xmlns="urn:jboss:domain:datasources:3.0">配置项目,在<drivers>里增加oracle驱动

增加

<driver name="oracle" module="com.oracle.ojdbc14">

    <driver-class>oracle.jdbc.OracleDriver</driver-class>

    <xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>

</driver>

 

2、配置XA数据源

配置两个xa的数据源,暂时都是数据库,以后学会JMS后再加上。

 

配置 1.2.1  oracle数据源-intf

<xa-datasource jndi-name="java:jboss/datasources/intfDS" pool-name="intf" enabled="true" use-ccm="false">

<xa-datasource-property name="URL">

        jdbc:oracle:thin:@127.0.0.1:1521:orcl

</xa-datasource-property>

    <driver>oracle</driver>

    <xa-pool>

        <is-same-rm-override>false</is-same-rm-override>

        <interleaving>false</interleaving>

        <pad-xid>false</pad-xid>

        <wrap-xa-resource>true</wrap-xa-resource>

    </xa-pool>

    <security>

        <user-name>intf</user-name>

        <password>intf</password>

    </security>

    <validation>

        <validate-on-match>false</validate-on-match>

        <background-validation>false</background-validation>

           <background-validation-millis>0</background-validation-millis>

    </validation>

    <statement>

        <prepared-statement-cache-size>0</prepared-statement-cache-size>

        <share-prepared-statements>false</share-prepared-statements>

</statement>

</xa-datasource>

 

配置 2.2  oracle数据源-cx

<xa-datasource jndi-name="java:jboss/datasources/cxDS" pool-name="cx" enabled="true" use-ccm="false">

    <xa-datasource-property name="URL">

        jdbc:oracle:thin:@127.0.0.1:1521:orcl

    </xa-datasource-property>

    <driver>oracle</driver>

    <xa-pool>

        <is-same-rm-override>false</is-same-rm-override>

        <interleaving>false</interleaving>

        <pad-xid>false</pad-xid>

        <wrap-xa-resource>true</wrap-xa-resource>

    </xa-pool>

    <security>

        <user-name>congxing</user-name>

        <password>congxing</password>

    </security>

    <recovery>

        <recover-credential>

            <user-name>congxing</user-name>

            <password>congxing</password>

        </recover-credential>

    </recovery>

    <validation>

        <validate-on-match>false</validate-on-match>

        <background-validation>false</background-validation>

        <background-validation-millis>0</background-validation-millis>

    </validation>

    <statement>

        <prepared-statement-cache-size>0</prepared-statement-cache-size>

        <share-prepared-statements>false</share-prepared-statements>

    </statement>

</xa-datasource>

 

3、配置JPA-persistence.xml

配置两个persistence-unit,分别使用上面的数据源

 

配置 1.3.1   intf持久化单元

<persistence-unit name="intf" transaction-type="JTA">

   <jta-data-source>java:jboss/datasources/intfDS</jta-data-source>

</persistence-unit>

 

配置 1.3.2  cx持久化单元

<persistence-unit name="cx" transaction-type="JTA">

   <jta-data-source>java:jboss/datasources/cxDS</jta-data-source>

</persistence-unit>

 

4、配置EntityManagerFactory

使用spring自动注入entitymanger(如果用EJB的话有容器注入),由于有两个数据源,定义两个EM

 

配置 1.4.1   intf EntityManagerFactory

<bean id="emf_intf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">

    <property name="persistenceUnitName" value="intf"/>

</bean>

 

 

 

配置 1.4.1   cx EntityManagerFactory

<bean id="emf_cx" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">

    <property name="persistenceUnitName" value="cx"/>

</bean>

 

5、配置JTA事务管理器

配置JTA事务管理器,并自动注入到业务层

 

配置 1.5.1   JTA事务管理器

<!-- 事务管理器配置-->

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />

 

配置 1.5.2   事务传播级别设置

<!-- aop事务属性设置-->

<aop:config>

    <aop:advisor pointcut="execution(* com.rbc.lcp..*.service.*Impl.*(..))" advice-ref="txAdvice"/>

</aop:config>

   <tx:advice id="txAdvice">

    <tx:attributes>

       <tx:method name="*" propagation="REQUIRED" rollback-for="Exception,RuntimeException" />

    </tx:attributes>

</tx:advice>

 

配置 1.5.3   事务自动注入设置

<!-- 使用annotation注入事务 -->

<tx:annotation-driven transaction-manager="transactionManager" />

 

6、配置Spring Data JPA

定义项目中各模块使用哪个数据源,将EM注入到对应的模块中。

 

配置 1.6.1   模块1使用数据库源1(intf)

<jpa:repositories base-package="com.rbc.lcp.manager.**.repository"

   entity-manager-factory-ref="emf_intf"

   transaction-manager-ref="transactionManager"

   />

 

配置 1.6.2   模块2使用数据库源2(cx)

<jpa:repositories base-package="com.rbc.lcp.manager2.**.repository"

   entity-manager-factory-ref="emf_cx"

   transaction-manager-ref="transactionManager"

   />

 

二、Atomikos实现JTA

使用atomikos就不需求依赖容器,这样可以使用tomcat,方便日常开发测试,和wildfly配置上大同小异,主要是数据源和事务管理器上差别较大。

由于项目使用的是hibernate4,所以需要atomikos4的支持,网上的教程都是atomikos3+hibernate3,写本文时atomikos4只是测试版,还没发布正式稳定版,资料很少,所以也花了不少时间才配置成功。

1、安装atomikos

配置 2.1.1   在工程pom.xml中引入atomikos依赖

<dependency>

   <groupId>com.atomikos</groupId>

   <artifactId>atomikos-util</artifactId>

   <version>4.0.0M4</version>

</dependency>

<dependency>

   <groupId>com.atomikos</groupId>

   <artifactId>transactions-api</artifactId>

   <version>4.0.0M4</version>

</dependency>

<dependency>

   <groupId>com.atomikos</groupId>

   <artifactId>transactions-jta</artifactId>

   <version>4.0.0M4</version>

</dependency>

<dependency>

   <groupId>com.atomikos</groupId>

   <artifactId>transactions</artifactId>

   <version>4.0.0M4</version>

</dependency>

<dependency>

   <groupId>com.atomikos</groupId>

   <artifactId>transactions-jdbc</artifactId>

   <version>4.0.0M4</version>

</dependency>

<dependency>

   <groupId>com.atomikos</groupId>

   <artifactId>transactions-hibernate4</artifactId>

   <version>4.0.0M4</version>

</dependency>

 

 

 

2、配置XA数据源

配置 2.2.1   配置数据源

<!-- 两个数据源的通用配置,方便下面直接引用 -->

<bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"

    init-method="init" destroy-method="close" abstract="true"  depends-on="txService">

    <property name="minPoolSize" value="10" />

    <property name="maxPoolSize" value="30" />

</bean>

 

<!-- 配置第一个数据源 -->

<bean id="intf_ds" parent="abstractXADataSource">

    <!-- value只要各个数据源不同就行,随便取名 -->

    <property name="uniqueResourceName" value="oracle/intf" />

    <property name="xaDataSourceClassName"

        value="oracle.jdbc.xa.client.OracleXADataSource" />

    <property name="xaProperties">

        <props>

            <prop key="URL">jdbc:oracle:thin:@127.0.0.1:1521:orcl</prop>

            <prop key="user">intf</prop>

            <prop key="password">intf</prop>

        </props>

    </property>

</bean>

 

<!-- 配置第二个数据源 -->

<bean id="cx_ds" parent="abstractXADataSource">

    <!-- value只要各个数据源不同就行,随便取名 -->

    <property name="uniqueResourceName" value="oracle/cx" />

    <property name="xaDataSourceClassName"

        value="oracle.jdbc.xa.client.OracleXADataSource" />

    <property name="xaProperties">

        <props>

            <prop key="URL">jdbc:oracle:thin:@127.0.0.1:1521:orcl</prop>

            <prop key="user">congxing</prop>

            <prop key="password">congxing</prop>

        </props>

    </property>

</bean>

 

3、配置JPA-persistence.xml

配置JPA持久化单元,注意和wildfly的区别,这里并不需要指定数据源。

 

配置 2.3.1   intf持久化单元

<persistence-unit name="intf" transaction-type="JTA">

</persistence-unit>

 

配置 2.3.2  cx持久化单元

<persistence-unit name="cx" transaction-type="JTA">

</persistence-unit>

 

4、配置EntityManagerFactory

注意和wildfly的区别,数据源也在这里配置

 

配置 2.4.1   intf EntityManagerFactory

<bean id="emf_intf"

    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="jtaDataSource" ref="intf_ds" />

    <property name="persistenceUnitName" value="intf" />

    <property name="jpaVendorAdapter">

       <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">

           <property name="databasePlatform"

value="org.hibernate.dialect.Oracle10gDialect" />

       </bean>

    </property>

    <property name="jpaPropertyMap">

       <props>

           <prop key="hibernate.current_session_context_class">jta</prop>

           <prop key="javax.persistence.transactionType">jta</prop>

           <prop key="hibernate.transaction.jta.platform">

           com.atomikos.icatch.jta.hibernate4.AtomikosPlatform

           </prop>

           <prop key="hibernate.search.autoregister_listeners">false</prop>

       </props>

    </property>

</bean>

 

配置 2.4.1   cx EntityManagerFactory

<bean id="emf_cx"

    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="jtaDataSource" ref="cx_ds" />

    <property name="persistenceUnitName" value="cx" />

    <property name="jpaVendorAdapter">

       <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">

           <property name="databasePlatform"

              value="org.hibernate.dialect.Oracle10gDialect" />

       </bean>

    </property>

    <property name="jpaPropertyMap">

       <props>

           <prop key="hibernate.current_session_context_class">jta</prop>

           <prop key="javax.persistence.transactionType">jta</prop>

           <prop key="hibernate.transaction.jta.platform">

           com.atomikos.icatch.jta.hibernate4.AtomikosPlatform

           </prop>

           <prop key="hibernate.search.autoregister_listeners">false</prop>

       </props>

    </property>

</bean>

 

5、配置JTA事务管理器

配置 2.5.1   JTA事务管理器

<bean id="txService" class="com.atomikos.icatch.config.UserTransactionServiceImp"

    init-method="init" destroy-method="shutdownWait">

</bean>

 

<bean id="txManager" class="com.atomikos.icatch.jta.UserTransactionManager"

    depends-on="txService" />

 

<bean id="userTx" class="com.atomikos.icatch.jta.UserTransactionImp"

    depends-on="txService" />

 

<bean id="transactionManager"

    class="org.springframework.transaction.jta.JtaTransactionManager">

    <property name="userTransaction" ref="userTx"></property>

    <property name="transactionManager" ref="txManager"></property>

</bean>

 

配置 2.5.2   事务传播级别设置

<!-- aop事务属性设置-->

<aop:config>

    <aop:advisor pointcut="execution(* com.rbc.lcp..*.service.*Impl.*(..))" advice-ref="txAdvice"/>

</aop:config>

   <tx:advice id="txAdvice">

    <tx:attributes>

       <tx:method name="*" propagation="REQUIRED" rollback-for="Exception,RuntimeException" />

    </tx:attributes>

</tx:advice>

 

配置 2S.5.3   事务自动注入设置

<!-- 使用annotation注入事务 -->

<tx:annotation-driven transaction-manager="transactionManager" />

 

6、配置Spring Data JPA

和前面wildfly一样的,就不贴了。

 

 

参考资料:

http://www.ibm.com/developerworks/cn/java/j-lo-jta/

http://blog.trixi.cz/2011/11/jta-transaction-manager-atomikos-or-bitronix/

https://wiki.kuali.org/display/KULRICE/Replacing+JOTM+and+XAPool+with+Atomikos