最近一直在研究怎麼實現分佈式事務,花了很多時間,測試工程啓停測試了無數次,最終實現的時候其實也就是寫一些配置文件,對於工程代碼沒什麼影響。目前研究還不是很深刻,對於全面崩潰恢復如何實現和測試還不清楚。本文先介紹基礎的實現。 java
當業務須要在一個事務中操做多個不一樣的資源,例如多個數據庫,消息隊列,緩存等,那麼就須要使用分佈式事務了。在Java中通常建議使用JTA,這樣開發人員就不用關心什麼叫XA協議,什麼是兩階段提交協議。要使用JTA須要容器的支持,例如使用JBOSS,WebSphere;或者使用第三方組件例如JOTM、Atomikos。 spring
JBOSS AS如今更名叫Wildfly了,以便和JBOSS EAP區分,後文我也改叫Wildfly。JOTM看起來是個死項目,我不打算使用。 數據庫
因爲目前開發框架基於spring+JPA設計,因此本文的配置主要是在spring中。其實用EJB的話配置更簡單,但須要容器支持。 api
a) 首先按照如下路徑新增目錄: 緩存
wildfly-9.0.0.CR1\modules\system\layers\base\com\oracle\ojdbc14\main tomcat
b) 把驅動文件ojdbc14.jar複製到此目錄下 session
c) 在main目錄下新增配置文件module.xml oracle
mudule.xml 框架
<?xml version="1.0" encoding="UTF-8"?> dom <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> |
配置兩個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> |
配置兩個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> |
使用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> |
配置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" /> |
定義項目中各模塊使用哪一個數據源,將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就不需求依賴容器,這樣可使用tomcat,方便平常開發測試,和wildfly配置上大同小異,主要是數據源和事務管理器上差異較大。
因爲項目使用的是hibernate4,因此須要atomikos4的支持,網上的教程都是atomikos3+hibernate3,寫本文時atomikos4只是測試版,還沒發佈正式穩定版,資料不多,因此也花了很多時間才配置成功。
配置 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.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> |
配置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> |
注意和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> |
配置 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" /> |
和前面wildfly同樣的,就不貼了。