基于spring,Atomikos,mybatis的分布式动态数据源JTA实现

     本文的几个关键词,分布式数据源,数据源的动态寻找,分布式事务JTA实现。
     对于一些较大规模的应用,单个数据源是无法支撑起庞大的用户量,需要引入多数据源,水平层面进行分库分表,降低单个DB的负载。接下来,我们程序里里面需 要管理不同数据源之前的程序调用,保证功能是WORK的。另外,跨库就意味着之前单DB的事务就失效了,所以J2EE提出了JTA,分布式的事务管理,往 简单了说,就是2步提交(two phase),比单步提交更苛刻。实际上他有两个容器来管理,一个是资源管理器,一个是事务管理。小伙伴们可以发现,这是一个环环相扣的过程。想解决一个 问题,你就得解决这几个相关的问题。以下代码,我也是参考了前辈们的思想,进行了改造。

    第一步:XA数据源定义
     选定义一个抽象的父类源,这样子类可以直接继承
  
 1  <!-- 两个数据源的功用配置,方便下面直接引用 -->
 2      <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
 3              destroy-method="close" abstract="true">
 4         <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
 5         <property name="poolSize" value="10" />
 6         <property name="minPoolSize" value="10"/>
 7         <property name="maxPoolSize" value="30"/>
 8         <property name="borrowConnectionTimeout" value="60"/>
 9         <property name="reapTimeout" value="20"/>
10         <!-- 最大空闲时间 -->
11         <property name="maxIdleTime" value="60"/>
12         <property name="maintenanceInterval" value="60" />
13         <property name="loginTimeout" value="60"/>
14         <property name="logWriter" value="60"/>
15         <property name="testQuery">
16             <value>select 1</value>
17         </property>
18         
19     </bean>
20 
   A源
     
 1 <!-- 配置第一个数据源 -->
 2     <bean id="dataSource_a" parent="abstractXADataSource">
 3     <!-- value只要两个数据源不同就行,随便取名 -->
 4         <property name="uniqueResourceName" value="mysql/sitestone" />
 5         <property name="xaDataSourceClassName"
 6             value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
 7         <property name="xaProperties">
 8             <props>
 9                 <prop key="URL">${jdbc.url.spider}</prop>
10                 <prop key="user">${jdbc.username}</prop>
11                 <prop key="password">${jdbc.password}</prop>
12             </props>
13         </property>
14     </bean>
   
B   源

   
 1 <!-- 配置第二个数据源-->
 2     <bean id="dataSource_b" parent="abstractXADataSource">
 3 <!-- value只要两个数据源不同就行,随便取名 -->
 4         <property name="uniqueResourceName" value="mysql/sitesttwo" />
 5         <property name="xaDataSourceClassName"
 6             value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
 7         <property name="xaProperties">
 8             <props>
 9                <prop key="URL">${jdbc_tb.url.spider}</prop>
10                 <prop key="user">${jdbc_tb.username}</prop>
11                 <prop key="password">${jdbc_tb.password}</prop>
12             </props>
13         </property>
14     </bean>

基于SPRING的AbstractRoutingDataSource动态数据路由定义
1   <bean name="dynamicDatasource" class="com.***.spring.datasource.CustomerDatasource">
2         <property name="targetDataSources">
3             <map>
4                 <entry key="ds_1" value-ref="dataSource_a"/>
5                 <entry key="ds_2" value-ref="dataSource_b"/>
6             </map>
7         </property>
8         <property name="defaultTargetDataSource" ref="dataSource_a"    />
9     </bean>
我这里是使用MYBATIS来进行ORM映射,配置如下
 1  <bean id="sqlSessionFactorya" class="org.mybatis.spring.SqlSessionFactoryBean">
 2         <property name="dataSource" ref="dataSource_a"/>
 3          <property name="typeAliasesPackage" value="com.****.spring.dschange.bean" />
 4          <!-- mapper和resultmap配置路径 --> 
 5         <property name="mapperLocations">
 6             <list>
 7                 <!-- 表示在com.**目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 --> 
 8                 <value>classpath:com/***/spring/dschange/mapper/ShopMapper.xml</value>
 9             </list>
10         </property>
11     </bean>
12     <bean id="sqlSessionFactoryb" class="org.mybatis.spring.SqlSessionFactoryBean">
13         <property name="dataSource" ref="dataSource_b"/>
14          <property name="typeAliasesPackage" value="com.****.spring.dschange.bean" />
15           <!-- mapper和resultmap配置路径 --> 
16         <property name="mapperLocations">
17             <list>
18                 <!-- 表示在com.***目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 --> 
19                 <value>classpath:com/***/spring/dschange/mapper/ShopMapper.xml</value>
20             </list>
21         </property>
22     </bean>
接下来,一个比较关键的地方是对MYBATIS的CustomSqlSessionTemplate的重写,主要是引入动态数据源sqlSessionFactory。针对不同的数据库,调用其对应的会话工厂,这对JTA是否启用,比较重要。
 1  @Override
 2     public SqlSessionFactory getSqlSessionFactory() {
 3  
 4         SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(DataSourceKeyHolder.getDataSourceKey());
 5         if (targetSqlSessionFactory != null) {
 6             return targetSqlSessionFactory;
 7         } else if (defaultTargetSqlSessionFactory != null) {
 8             return defaultTargetSqlSessionFactory;
 9         } else {
10             Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
11             Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
12         }
13         return this.sqlSessionFactory;
14     }
XML配置
 1  <!-- 配置自定义的SqlSessionTemplate模板,注入相关配置 -->
 2     <bean id="sqlSessionTemplate" class="com.amos.spring.mybatis.CustomSqlSessionTemplate" scope="prototype">
 3         <constructor-arg ref="sqlSessionFactorya" />
 4         <property name="targetSqlSessionFactorys">
 5             <map>     
 6                 <entry value-ref="sqlSessionFactorya" key="ds_1"/>
 7                 <entry value-ref="sqlSessionFactoryb" key="ds_2"/>
 8             </map> 
 9         </property>
10     </bean>

扫描配置
1  <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 1     <!-- jta -->
 2     <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
 3         init-method="init" destroy-method="close">
 4         <property name="forceShutdown">
 5             <value>true</value>
 6         </property>
 7     </bean>
 8  
 9     <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
10         <property name="transactionTimeout" value="300" />
11     </bean>
12  
13     <bean id="springTransactionManager"
14         class="org.springframework.transaction.jta.JtaTransactionManager">
15         <property name="transactionManager">
16             <ref bean="atomikosTransactionManager" />
17         </property>
18         <property name="userTransaction">
19             <ref bean="atomikosUserTransaction" />
20         </property>
21     </bean>
22  <tx:annotation-driven transaction-manager="springTransactionManager" proxy-target-class="true" />
2         <property name="basePackage" value="com.****.spring.dschange.mapper" />
3         <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
4         <property name="markerInterface" value="com.*****.spring.dschange.mapper.SqlMapper"/>
5     </bean>

最后就是JTA的实现配置
 1     <!-- jta -->
 2     <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
 3         init-method="init" destroy-method="close">
 4         <property name="forceShutdown">
 5             <value>true</value>
 6         </property>
 7     </bean>
 8  
 9     <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
10         <property name="transactionTimeout" value="300" />
11     </bean>
12  
13     <bean id="springTransactionManager"
14         class="org.springframework.transaction.jta.JtaTransactionManager">
15         <property name="transactionManager">
16             <ref bean="atomikosTransactionManager" />
17         </property>
18         <property name="userTransaction">
19             <ref bean="atomikosUserTransaction" />
20         </property>
21     </bean>
22  <tx:annotation-driven transaction-manager="springTransactionManager" proxy-target-class="true" />
以上,XML配置相关的东西已经完成。
具体的代码,请点击GITHUB查看https://github.com/igool/spring-jta-mybatis

相关推荐