JTA分布式事务实践
最近一直在研究怎么实现分布式事务,花了不少时间,测试工程启停测试了无数次,最终实现的时候其实也就是写一些配置文件,对于工程代码没什么影响。目前研究还不是很深入,对于全面崩溃恢复如何实现和测试还不清楚。本文先介绍基础的实现。
当业务需要在一个事务中操作多个不同的资源,例如多个数据库,消息队列,缓存等,那么就需要使用分布式事务了。在java中一般建议使用JTA,这样开发人员就不用关心什么叫XA协议,什么是两阶段提交协议。要使用JTA需要容器的支持,例如使用JBOSS,WebSphere;或者使用第三方组件例如JOTM、Atomikos。
JBOSS AS现在改名叫Wildfly了,以便和JBOSS EAP区分,后文我也改叫Wildfly。JOTM看起来是个死项目,我不打算使用。
由于目前开发框架基于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> |
打开wildfly的standalone.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事务管理器,并自动注入到业务层
<!-- 事务管理器配置--> <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