基于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

你可能感兴趣的:(spring,mybatis,atomikos)