背景:项目架构使用了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