背景:項目架構使用了SpringMvc + Mybatis,同時在使用多數據源的時候需要滿足事務一致性
通俗的說法是:項目中配置了多個數據源,並且在一個service方法中使用多個數據源的時候需要保證事務一致性。
網上的主流資料大概講解了兩種spring對分布式事務的實現:jotm和Atomikos,需要注意的是使用jotm的時候需要用到一個類org.springframework.transaction.jta.JotmFactoryBean然而在spring 3.x之后移除了這個類,所以我采用了Atomikos的方式。
實現過程如下:
1、在使用了Atomikos之后需要注意DataSource不能再使用c3p0 之類的驅動了,需要用到com.atomikos.jdbc.AtomikosDataSourceBean,下面是兩個DataSource的配置示例:
1 <bean id="dataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> 2 <property name="uniqueResourceName" value="XA1DBMS1" /> 3 <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> 4 <property name="xaProperties"> 5 <props> 6 <prop key="URL">${dataPortal_mysql_jdbc_url}</prop> 7 <prop key="user">${dataPortal_mysql_jdbc_user}</prop> 8 <prop key="password">${dataPortal_mysql_jdbc_password}</prop> 9 </props> 10 </property> 11 <property name="poolSize" value="3" /> 12 <property name="minPoolSize" value="3" /> 13 <property name="maxPoolSize" value="5" /> 14 </bean> 15 16 <bean id="webDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> 17 <property name="uniqueResourceName" value="XA1DBMS2" /> 18 <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> 19 <property name="xaProperties"> 20 <props> 21 <prop key="URL">${web.jdbc.url}</prop> 22 <prop key="user">${web.jdbc.username}</prop> 23 <prop key="password">${web.jdbc.password}</prop> 24 </props> 25 </property> 26 <property name="poolSize" value="3" /> 27 <property name="minPoolSize" value="3" /> 28 <property name="maxPoolSize" value="5" /> 29 </bean>
這里一些其他的參數,比如最大連接數,超時時間的參數我是在com.atomikos.jdbc.AtomikosDataSourceBean源碼中找到的:
兩個DataSource中webDataSource我采用的是spring的JdbcTemplate去寫sql而dataSource則配置使用了mybatis的方式
2、兩種不同的方式配置如下:
1 <!-- web庫jdbcTemplate --> 2 <bean id="webJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 3 <property name="dataSource" ref="webDataSource" /> 4 </bean> 5 <!-- gmt庫jdbcTemplate --> 6 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 7 <property name="dataSource"> 8 <ref bean="dataSource" /> 9 </property> 10 </bean> 11 12 <!-- 創建SqlSessionFactory,同時指定數據源 --> 13 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 14 <property name="dataSource" ref="dataSource" /> 15 <property name="mapperLocations" value="classpath:com/sincetimes/modernship/**/dao/*.xml"/> 16 </bean> 17 18 <!-- mybatis自動掃描器 --> 19 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 20 <property name="basePackage" value="com.sincetimes.modernship.dao" /> 21 <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> 22 </bean> 23 24 <!-- DAO使用mybatis進行數據庫訪問操作 --> 25 <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> 26 <constructor-arg index="0" ref="sqlSessionFactory" /> 27 </bean>
3、在配置事務的時候,我們可以使用@Transactional注解,也可以寫aop
1 <!-- 分布式事務 --> 2 <bean id="userTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> 3 <property name="transactionTimeout" value="300" /> 4 </bean> 5 6 <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 7 <property name="userTransaction" ref="userTransaction" /> 8 </bean> 9 10 <tx:annotation-driven transaction-manager="springTransactionManager"/> 11 12 <aop:config> 13 <aop:pointcut id="baseServiceMethods" expression="execution(* com.sincetimes..impl..*.*(..))" /> 14 <aop:advisor pointcut-ref="baseServiceMethods" advice-ref="txAdvice"/> 15 </aop:config> 16 17 <!-- 配置事務的傳播特性 --> 18 <tx:advice id="txAdvice" transaction-manager="springTransactionManager"> 19 <tx:attributes> 20 <tx:method name="query*" propagation="REQUIRED" read-only="true" /> 21 <tx:method name="get*" propagation="REQUIRED" read-only="true" /> 22 <tx:method name="find*" propagation="REQUIRED" read-only="true" /> 23 <tx:method name="list*" propagation="REQUIRED" read-only="true" /> 24 <tx:method name="count*" propagation="REQUIRED" read-only="true" /> 25 <tx:method name="insert*" propagation="REQUIRED" /> 26 <tx:method name="add*" propagation="REQUIRED" /> 27 <tx:method name="del*" propagation="REQUIRED" /> 28 <tx:method name="save*" propagation="REQUIRED" /> 29 <tx:method name="update*" propagation="REQUIRED" /> 30 <tx:method name="edit*" propagation="REQUIRED" /> 31 <tx:method name="enable*" propagation="REQUIRED" /> 32 <tx:method name="upload*" propagation="REQUIRED" /> 33 </tx:attributes> 34 </tx:advice>
分布式事務中的重點是事務管理器。在Atomikos中對應的就是 com.atomikos.icatch.jta.UserTransactionManager
4、如果項目使用了log4j的話,並且日志級別為info,在項目啟動后會發現有很多atomikos的info日志打印出來,這個時候在log4j的配置文件中增加一個配置:log4j.logger.com.atomikos = error 即可。
這里參考了網上找到的資料:https://my.oschina.net/pingpangkuangmo/blog/413518