spring+mybatis+atomikos 實現JTA事務


1. 選擇哪種transaction manager?
     在單數據源情況下,JDBC,Hibernate,ibatis等自帶的 transaction manager已能用於處理事務。
    但當設計多種數據源的事務處理時,上面的transaction manager就沒法用了。這個時候可選事務管理組件有:Bitronix,SimpleJTA,Tyrex (dead?), JOTM (used in Jonas),GeronimoTM/Jencks (used in Geronimo),JBossTS (used in JBoss) andAtomikos. 其中Atomikos 被大多數人所推薦。

2. 最近弄一個框架,使用spring3.0.5+mybatis3.0.5,需要訪問多庫,要應用分布式事務JTA,這是用atomikos 3.70版本,並把配置做一下記錄。

配置多個數據源
 
Xml代碼 
<!-- 第一個數據庫 -->  
     <bean id="dataSource" class="com.atomikos.jdbc.SimpleDataSourceBean"  
         init-method="init" destroy-method="close">  
         <property name="uniqueResourceName" value="mysql/main" />  
         <property name="xaDataSourceClassName"  
             value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />  
         <property name="xaDataSourceProperties"  
             value="URL=${jdbc.url.a};user=${jdbc.username.a};password=${jdbc.password.a}" />  
         <property name="exclusiveConnectionMode" value="true" />  
         <property name="connectionPoolSize" value="10" />  
         <property name="validatingQuery">  
             <value>SELECT 1</value>  
         </property>  
     </bean>  
     <!-- 第二個數據庫 -->  
     <bean id="dataSourceB" class="com.atomikos.jdbc.SimpleDataSourceBean"  
         init-method="init" destroy-method="close">  
         <property name="uniqueResourceName" value="mysql/news" />  
         <property name="xaDataSourceClassName"  
             value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />  
         <property name="xaDataSourceProperties"  
             value="URL=${jdbc.url.b};user=${jdbc.username.b};password=${jdbc.password.b}" />  
         <property name="exclusiveConnectionMode" value="true" />  
         <property name="connectionPoolSize" value="10" />  
         <property name="validatingQuery">  
             <value>SELECT 1</value>  
         </property>  
     </bean>  
<!-- 第一個數據庫 -->
<bean id="dataSource" class="com.atomikos.jdbc.SimpleDataSourceBean"
init-method="init" destroy-method="close">
<property name="uniqueResourceName" value="mysql/main" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaDataSourceProperties"
value="URL=${jdbc.url.a};user=${jdbc.username.a};password=${jdbc.password.a}" />
<property name="exclusiveConnectionMode" value="true" />
<property name="connectionPoolSize" value="10" />
<property name="validatingQuery">
<value>SELECT 1</value>
</property>
</bean>
<!-- 第二個數據庫 -->
<bean id="dataSourceB" class="com.atomikos.jdbc.SimpleDataSourceBean"
init-method="init" destroy-method="close">
<property name="uniqueResourceName" value="mysql/news" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaDataSourceProperties"
value="URL=${jdbc.url.b};user=${jdbc.username.b};password=${jdbc.password.b}" />
<property name="exclusiveConnectionMode" value="true" />
<property name="connectionPoolSize" value="10" />
<property name="validatingQuery">
<value>SELECT 1</value>
</property>
</bean>
 
 
配置mybatis的SessionFactory
 
 
Xml代碼 
<bean id="sqlSessionFactoryB" class="org.mybatis.spring.SqlSessionFactoryBean">  
     <property name="configLocation" value="classpath:mybatis/mybatis-config-b.xml" />  
     <property name="dataSource" ref="dataSourceB" />  
 </bean>  
   
 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
     <property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />  
     <property name="dataSource" ref="dataSource" />  
 </bean>  
<bean id="sqlSessionFactoryB" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis/mybatis-config-b.xml" />
<property name="dataSource" ref="dataSourceB" />
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
<property name="dataSource" ref="dataSource" />
</bean>
 這里使用的是SessionFactory,不是org.springframework.orm.ibatis.SqlMapClientFactoryBean,在mybatis3中用SqlMapClientFactoryBean匯報com.ibatis.common.xml.NodeletException 異常。
 
configLocation 對應的mybatis配置,跟平時配置一樣。
Xml代碼 
<?xml version="1.0" encoding="UTF-8" ?>  
 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">  
 <configuration>  
     <typeAliases>  
         <typeAlias alias="User"  type="com.lantii.domain.User"/>  
     </typeAliases>  
     <mappers>  
         <mapper resource="com/lantii/dao/UserMapper.xml" />  
     </mappers>  
 </configuration>    
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="User"  type="com.lantii.domain.User"/>
</typeAliases>
<mappers>
<mapper resource="com/lantii/dao/UserMapper.xml" />
</mappers>
</configuration>

 事務這塊用spring管理atomikos
Xml代碼 
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"  
         init-method="init" destroy-method="close">  
         <property name="forceShutdown">  
             <value>true</value>  
         </property>  
     </bean>  
   
     <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">  
         <property name="transactionTimeout" value="300" />  
     </bean>  
   
     <bean id="springTransactionManager"  
         class="org.springframework.transaction.jta.JtaTransactionManager">  
         <property name="transactionManager">  
             <ref bean="atomikosTransactionManager" />  
         </property>  
         <property name="userTransaction">  
             <ref bean="atomikosUserTransaction" />  
         </property>  
     </bean>  
   
     <aop:aspectj-autoproxy />  
   
     <aop:config  proxy-target-class="true">  
         <aop:advisor pointcut="execution(* *com.lantii.service..*(..))"  
             advice-ref="txAdvice" />  
     </aop:config>  
   
     <tx:advice id="txAdvice" transaction-manager="springTransactionManager">  
         <tx:attributes>  
             <tx:method name="get*"  propagation="REQUIRED"  read-only="true" />  
             <tx:method name="find*"  propagation="REQUIRED"  read-only="true" />  
             <tx:method name="has*"  propagation="REQUIRED"  read-only="true" />  
             <tx:method name="locate*"  propagation="REQUIRED"  read-only="true" />  
             <tx:method name="*"  propagation="REQUIRED" rollback-for="Exception"  />  
         </tx:attributes>  
     </tx:advice>  
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<property name="forceShutdown">
<value>true</value>
</property>
</bean>

<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300" />
</bean>

<bean id="springTransactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager">
<ref bean="atomikosTransactionManager" />
</property>
<property name="userTransaction">
<ref bean="atomikosUserTransaction" />
</property>
</bean>

<aop:aspectj-autoproxy />

<aop:config  proxy-target-class="true">
<aop:advisor pointcut="execution(* *com.lantii.service..*(..))"
advice-ref="txAdvice" />
</aop:config>

<tx:advice id="txAdvice" transaction-manager="springTransactionManager">
<tx:attributes>
<tx:method name="get*"  propagation="REQUIRED"  read-only="true" />
<tx:method name="find*"  propagation="REQUIRED"  read-only="true" />
<tx:method name="has*"  propagation="REQUIRED"  read-only="true" />
<tx:method name="locate*"  propagation="REQUIRED"  read-only="true" />
<tx:method name="*"  propagation="REQUIRED" rollback-for="Exception"  />
</tx:attributes>
</tx:advice>
 
Mapper的管理及注入
Xml代碼 
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">  
         <property name="sqlSessionFactory" ref="sqlSessionFactory" />  
         <property name="mapperInterface" value="com.lantii.dao.UserMapper" />  
     </bean>  
        
     <bean id="roleMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">  
         <property name="sqlSessionFactory" ref="sqlSessionFactoryB" />  
         <property name="mapperInterface" value="com.lantii.dao.RoleMapper" />  
     </bean>  
   
     <bean id="userService" class="com.lantii.service.UserServiceImpl">  
         <property name="userMapper" ref="userMapper" />  
         <property name="roleMapper" ref="roleMapper" />  
     </bean>  
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
<property name="mapperInterface" value="com.lantii.dao.UserMapper" />
</bean>

<bean id="roleMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="sqlSessionFactoryB" />
<property name="mapperInterface" value="com.lantii.dao.RoleMapper" />
</bean>

<bean id="userService" class="com.lantii.service.UserServiceImpl">
<property name="userMapper" ref="userMapper" />
<property name="roleMapper" ref="roleMapper" />
</bean>  
atomikos的配置jta.properties,該文件放在應用classpath下面
Java代碼 
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory   
 com.atomikos.icatch.console_file_name = tm.out   
 com.atomikos.icatch.log_base_name = tmlog   
 com.atomikos.icatch.tm_unique_name = com.atomikos.spring.jdbc.tm   
 com.atomikos.icatch.console_log_level=WARN  
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.console_file_name = tm.out
com.atomikos.icatch.log_base_name = tmlog
com.atomikos.icatch.tm_unique_name = com.atomikos.spring.jdbc.tm
com.atomikos.icatch.console_log_level=WARN
  
這幾基本配置完畢,需要jar包
atomikos-util.jar
transactions.jar
transactions-jta.jar
transactions-jdbc-deprecated.jar
mybatis.jar
mybatis-spring.jar
cglib.2.2.2.jar
spring的jar包
 
在Service中,調用事務的方法不能try。。。catch事務的方法,否者不能回滾
如下帶面就會出現不會滾問題
Java代碼 
try{   
                 userMapper.addUser(user);   
                 roleMapper.addRole(role);   
             }catch(Exception){   
                    
             }  

3. 使用atomikos作分布式事務,連接池超時問題
 2012-11-15

mysql會自動斷開超過60秒的連接,而atomikos連接池沒有檢測機制去保持空閑的連接
造成連接池的連接超時掉了,以下是日志內容
com.mysql.jdbc.jdbc2.optional.MysqlXAException: Communications link failure


The last packet successfully received from the server was 157,969 milliseconds ago.  The last packet sent successfully to the server was 3 milliseconds ago.
at com.mysql.jdbc.jdbc2.optional.MysqlXAConnection.mapXAExceptionFromSQLException(MysqlXAConnection.java:604)
at com.mysql.jdbc.jdbc2.optional.MysqlXAConnection.dispatchCommand(MysqlXAConnection.java:583)
at com.mysql.jdbc.jdbc2.optional.MysqlXAConnection.start(MysqlXAConnection.java:523)
at com.atomikos.datasource.xa.XAResourceTransaction.resume(XAResourceTransaction.java:498)
at com.atomikos.datasource.xa.session.BranchEnlistedStateHandler.<init>(BranchEnlistedStateHandler.java:59)
at com.atomikos.datasource.xa.session.NotInBranchStateHandler.checkEnlistBeforeUse(NotInBranchStateHandler.java:65)
at com.atomikos.datasource.xa.session.TransactionContext.checkEnlistBeforeUse(TransactionContext.java:88)
at com.atomikos.datasource.xa.session.SessionHandleState.notifyBeforeUse(SessionHandleState.java:179)
at com.atomikos.jdbc.AtomikosConnectionProxy.enlist(AtomikosConnectionProxy.java:208)
at com.atomikos.jdbc.AtomikosConnectionProxy.invoke(AtomikosConnectionProxy.java:142)
google下看到幾個說修改mysql連接超時設置,我覺得mysql連接超時配置是不建議修改的,有沒有更好的方式解決呢

........................................................


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM