比尔云BierYun--阿里云最新优惠活动
阿里云优惠码丨阿里云代金券

Spring分布式事务在service中动态切换数据源

Spring分布式事务在service中动态切换数据源http://www.bieryun.com/1790.html

项目采用的是struts2的+弹簧+ ibatis的架构,下面是关键部分代码:

applicationContext.xml中:

[html] 查看纯文本

  1. <?xml version = “1.0” encoding = “UTF-8” ?>
  2. < beans xmlns = “http://www.springframework.org/schema/beans”
  3.        xmlns:xsi = “http://www.w3.org/2001/XMLSchema-instance”
  4.        xmlns:context = “http://www.springframework.org/schema/context”
  5.        xmlns:aop = “http://www.springframework.org/schema/aop”
  6.        xmlns:tx = “http://www.springframework.org/schema/tx”
  7.        xsi:schemaLocation =“http://www.springframework.org/schema/beans
  8.            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  9.            http://www.springframework.org/schema/context
  10.            http://www.springframework.org/schema/context/spring-context-2.5.xsd
  11.            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
  12.            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd“
  13.            default-autowire = “byName” default-lazy-init = “false” >
  14.     < context:component-scan base-package = “com.ssi。*” />
  15.     <! – 属性文件读入 – >
  16.     < bean id = “propertyConfigurer” class = “org.springframework.beans.factory.config.PropertyPlaceholderConfigurer” >>
  17.         < property name = “locations” >
  18.             < list >
  19.                 < value > classpath *:jdbc.properties </ value >
  20.             </ list >
  21.         </ property >
  22.     </ bean >
  23.     <! – 配置sqlMapclient – >
  24.     < bean id = “sqlMapClient” class = “org.springframework.orm.ibatis.SqlMapClientFactoryBean” >
  25.         < property name = “configLocation” value = “classpath:ibatis-sqlmap-config.xml” />
  26.         < property name = “dataSource” ref = “dataSource” />
  27.     </ bean >
  28.     < bean id = “sqlMapClient1” class = “org.springframework.orm.ibatis.SqlMapClientFactoryBean” >
  29.         < property name = “configLocation” value = “classpath:ibatis-sqlmap-config.xml” />
  30.         < property name = “dataSource” ref = “db1” />
  31.     </ bean >
  32.     < bean id = “sqlMapClient2” class = “org.springframework.orm.ibatis.SqlMapClientFactoryBean” >
  33.         < property name = “configLocation” value = “classpath:ibatis-sqlmap-config.xml” />
  34.         < property name = “dataSource” ref = “db2” />
  35.     </ bean >
  36.     < bean id = “sqlMapClientCenter” class = “org.springframework.orm.ibatis.SqlMapClientFactoryBean” >
  37.         < property name = “configLocation” value = “classpath:ibatis-sqlmap-config.xml” />
  38.         < property name = “dataSource” ref = “center” />
  39.     </ bean >
  40.     < bean id = “dynamicSqlMapClientDaoSupport” class = “com.ssi.dao.DynamicSqlClientDaoSupport” >>
  41.         < property name = “targetSqlMapClients” >
  42.             < map >
  43.                 < entry key = “db1” value-ref = “sqlMapClient1” />
  44.                 < entry key = “db2” value-ref = “sqlMapClient2” />
  45.                 < entry key = “center” value-ref = “sqlMapClientCenter” />
  46.             </ map >
  47.         </ property >
  48.         < property name = “defaultSqlMapClient” ref = “sqlMapClientCenter” />
  49.     </ bean >
  50.     < bean id = “ibatisDaoSupport” class = “com.ssi.dao.IbatisDaoSupport” parent = “dynamicSqlMapClientDaoSupport” > </bean >
  51.     < bean id = “userDao” class = “com.ssi.dao.impl.UserDaoImpl” parent = “ibatisDaoSupport” > </ bean >
  52.     <! – 支持@AspectJ标记 – >
  53.     < aop:aspectj-autoproxy proxy-target-class = “true” />
  54.     <! – 配置JTA的事务管理器 – >
  55.     < bean id = “atomikosTransactionManager” class = “com.atomikos.icatch.jta.UserTransactionManager” init-method = “init” destroy-method = “close” >
  56.         < property name = “forceShutdown” value = “true” />
  57.     </ bean >
  58.     < bean id = “atomikosUserTransaction” class = “com.atomikos.icatch.jta.UserTransactionImp” >
  59.         < property name = “transactionTimeout” value = “300” />
  60.     </ bean >
  61.     < bean id = “springTransactionManager” class = “org.springframework.transaction.jta.JtaTransactionManager”>
  62.         < property name = “transactionManager” ref = “atomikosTransactionManager” />
  63.         < property name = “userTransaction” ref = “atomikosUserTransaction” />
  64.     </ bean >
  65.     <! – 配置通知 – >
  66.     < tx:advice id = “txAdvice” transaction-manager = “springTransactionManager” >
  67.         < tx:attributes >
  68.              < tx:method name = “*” rollback-for = “Exception,RuntimeException,com.ssi.exception.SystemException” propagation = “REQUIRED” />
  69.         </ tx:attributes >
  70.     </ tx:advice >
  71.     <! – 以AspectJ方式定义AOP – >
  72.     < aop:config >
  73.         < aop:advisor pointcut = “execution(* com.ssi.service .. * Service *。*(..))” advice-ref = “txAdvice”/>
  74.     </ aop:config >
  75.     <! – spring定时器任务开始 – >
  76.     < bean name = “job” class = “org.springframework.scheduling.quartz.JobDetailBean” >
  77.          < property name = “jobClass” >
  78.              < value > com.ssi.action.TimerAction </ value >
  79.          </ property >
  80.          < property name = “jobDataAsMap” >
  81.              < map >
  82.                   <! – timeout属性设定了当服务器启动后过10秒钟首次调用你的JobAction – >
  83.                   < entry key = “timeout” >
  84.                      < value > 10 </ value >
  85.                   </ entry >
  86.              </ map >
  87.          </ property >
  88.     </ bean >
  89.     < bean id = “cronTrigger” class = “org.springframework.scheduling.quartz.CronTriggerBean” >
  90.          < property name = “jobDetail” >
  91.              < ref bean = “job” />
  92.          </ property >
  93.          < property name = “cronExpression” >
  94.              < > 0 53 15?* MON-FRI </ value >
  95.          </ property >
  96.     </ bean >
  97.     < bean class = “org.springframework.scheduling.quartz.SchedulerFactoryBean” autowire = “no” >
  98.          < property name = “triggers” >
  99.              < list >
  100.                  < ref local = “cronTrigger” />
  101.              </ list >
  102.          </ property >
  103.     </ bean >
  104.     <! – spring定时器任务结束 – >
  105. </ beans >

 

[html] 查看纯文本

的applicationContext-datasource.xml

[html] 查看纯文本

  1. <?xml version = “1.0” encoding = “UTF-8” ?>
  2. < beans xmlns = “http://www.springframework.org/schema/beans”
  3.     xmlns:xsi = “http://www.w3.org/2001/XMLSchema-instance”
  4.     xmlns:aop = “http://www.springframework.org/schema/aop”
  5.     xmlns:tx = “http://www.springframework.org/schema/tx”
  6.     xsi:schemaLocation =“
  7.     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  8.     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
  9.     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd“ >
  10.     <! – 指定春天配置中用到的属性文件 – >
  11.     < bean id = “propertyConfig” class = “org.springframework.beans.factory.config.PropertyPlaceholderConfigurer”>>
  12.         < property name = “locations” >
  13.             < list >
  14.                 < value > classpath:jdbc.properties </ value >
  15.             </ list >
  16.         </ property >
  17.     </ bean >
  18.     <! – JTA数据源配置 – >
  19.     < bean id = “center” class = “com.atomikos.jdbc.AtomikosDataSourceBean” init-method = “init” destroy-method = “close” >
  20.         < property name = “uniqueResourceName” >
  21.             < > mysql / center </ value >
  22.         </ property >
  23.         < property name = “xaDataSourceClassName” >
  24.             < value > $ {jta.driver.className} </ value >
  25.         </ property >
  26.         < property name = “xaProperties” >
  27.             < 道具>
  28.                 < prop key = “url” > $ {center.jdbc.driver.url} </ prop >
  29.                 < prop key = “user” > $ {center.sql.user.name} </ prop >
  30.                 < prop key = “password” > $ {center.sql.user.password} </ prop >
  31.             </ 道具>
  32.         </ property >
  33.         < property name = “testQuery” value = “select 1” />
  34.         < property name = “poolSize” >
  35.             < value > $ {poolsize} </ value >
  36.         </ property >
  37.         < property name = “maxPoolSize” >
  38.             < value > $ {maxPoolSize} </ value >
  39.         </ property >
  40.         < property name = “borrowConnectionTimeout” > < value > $ {borrowConnectionTimeout} </ value > </ property >
  41.     </ bean >
  42.     < bean id = “db1” class = “com.atomikos.jdbc.AtomikosDataSourceBean” init-method = “init” destroy-method = “close”>
  43.         < property name = “uniqueResourceName” >
  44.             < value > mysql / db1 </ value >
  45.         </ property >
  46.         < property name = “xaDataSourceClassName” >
  47.             < value > $ {jta.driver.className} </ value >
  48.         </ property >
  49.         < property name = “xaProperties” >
  50.             < 道具>
  51.                 < prop key = “url” > $ {db1.jdbc.driver.url} </ prop >
  52.                 < prop key = “user” > $ {company.sql.user.name} </ prop >
  53.                 < prop key = “password” > $ {company.sql.user.password} </ prop >
  54.             </ 道具>
  55.         </ property >
  56.         < property name = “testQuery” value = “select 1” />
  57.         < property name = “poolSize” >
  58.             < value > $ {poolsize} </ value >
  59.         </ property >
  60.         < property name = “maxPoolSize” >
  61.             < value > $ {maxPoolSize} </ value >
  62.         </ property >
  63.         < property name = “borrowConnectionTimeout” > < value > $ {borrowConnectionTimeout} </ value > </ property >
  64.     </ bean >
  65.     < bean id = “db2” class = “com.atomikos.jdbc.AtomikosDataSourceBean” init-method = “init” destroy-method = “close”>
  66.         < property name = “uniqueResourceName” >
  67.             < value > mysql / db2 </ value >
  68.         </ property >
  69.         < property name = “xaDataSourceClassName” >
  70.             < value > $ {jta.driver.className} </ value >
  71.         </ property >
  72.         < property name = “xaProperties” >
  73.             < 道具>
  74.                 < prop key = “url” > $ {db2.jdbc.driver.url} </ prop >
  75.                 < prop key = “user” > $ {company.sql.user.name} </ prop >
  76.                 < prop key = “password” > $ {company.sql.user.password} </ prop >
  77.             </ 道具>
  78.         </ property >
  79.         < property name = “testQuery” value = “select 1” />
  80.         < property name = “poolSize” >
  81.             < value > $ {poolsize} </ value >
  82.         </ property >
  83.         < property name = “maxPoolSize” >
  84.             < value > $ {maxPoolSize} </ value >
  85.         </ property >
  86.         < property name = “borrowConnectionTimeout” > < value > $ {borrowConnectionTimeout} </ value > </ property >
  87.     </ bean >
  88.     < bean id = “dataSource” class = “com.ssi.datasource.DynamicDataSource” >
  89.         < property name = “targetDataSources” >
  90.             < map key-type = “java.lang.String” >
  91.                 < entry key = “db1” value-ref = “db1” />
  92.                 < entry key = “db2” value-ref = “db2” />
  93.                 < entry key = “center” value-ref = “center” />
  94.             </ map >
  95.         </ property >
  96.         < property name = “defaultTargetDataSource” ref = “center” />
  97.     </ bean >
  98. </ beans >

 

DynamicSqlClientDaoSupport.java

[java] 查看纯文本

  1.  com.ssi.dao;
  2. import  java.util.Map;
  3. import  javax.sql.DataSource;
  4. import  org.springframework.beans.factory.InitializingBean;
  5. import  org.springframework.dao.support.DaoSupport;
  6. import  org.springframework.orm.ibatis.SqlMapClientTemplate;
  7. import  org.springframework.util.Assert;
  8. import  com.ibatis.sqlmap.client.SqlMapClient;
  9. import  com.ssi.datasource.DbContextHolder;
  10. 公共 DynamicSqlClientDaoSupport  扩展 DaoSupport  实现 InitializingBean {
  11.     私人 SqlMapClientTemplate sqlMapClientTemplate =  新的 SqlMapClientTemplate();
  12.     私人 地图<String,SqlMapClient> targetSqlMapClients;
  13.     私人 SqlMapClient的defaultSqlMapClient;
  14.     private boolean  externalTemplate =  false ;
  15.     / **
  16.      *设置此DAO使用的JDBC数据源。
  17.      *不需要:SqlMapClient可能携带共享数据源。
  18.      * @see #setSqlMapClient
  19.      * /
  20.     public final void  setDataSource(DataSource dataSource){
  21.         如果 (!this .externalTemplate){
  22.         这个.sqlMapClientTemplate.setDataSource(dataSource);
  23.         }
  24.     }
  25.     / **
  26.      *返回此DAO使用的JDBC数据源。
  27.      * /
  28.     public final  DataSource getDataSource(){
  29.         返回这个.sqlMapClientTemplate.getDataSource();
  30.     }
  31.     / **
  32.      *设置iBATIS数据库层SqlMapClient使用。
  33.      *这个或者“sqlMapClientTemplate”是必需的。
  34.      * @see #setSqlMapClientTemplate
  35.      * /
  36.     public final void  setSqlMapClient(SqlMapClient sqlMapClient){
  37.         如果 (!this .externalTemplate){
  38.             这个.sqlMapClientTemplate.setSqlMapClient(sqlMapClient);
  39.         }
  40.     }
  41.     / **
  42.      *返回该模板使用的iBATIS数据库层SqlMapClient。
  43.      * /
  44.     public final  SqlMapClient getSqlMapClient(){
  45.         返回这个.sqlMapClientTemplate.getSqlMapClient();
  46.     }
  47.     / **
  48.      *明确地为此DAO设置SqlMapClientTemplate,
  49.      *作为指定SqlMapClient的替代方法。
  50.      * @see #setSqlMapClient
  51.      * /
  52.     public final void  setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate){
  53.         Assert.notNull(sqlMapClientTemplate,  “SqlMapClientTemplate不能为空” );
  54.         这个.sqlMapClientTemplate = sqlMapClientTemplate;
  55.         这个.externalTemplate =  true ;
  56.     }
  57.     / **
  58.      *返回此DAO的SqlMapClientTemplate,
  59.      *用SqlMapClient预先初始化或明确设置。
  60.      * /
  61.     public final  SqlMapClientTemplate getSqlMapClientTemplate(){
  62.       字符串dbtype = DbContextHolder.getDbType();
  63.       if (targetSqlMapClients!= null && targetSqlMapClients.containsKey(dbtype)){
  64.           SqlMapClient sqlMapClient = targetSqlMapClients.get(dbtype);
  65.           sqlMapClientTemplate =  新的 SqlMapClientTemplate(sqlMapClient);
  66.       }
  67.       返回这个.sqlMapClientTemplate;
  68.     }
  69.     @覆盖
  70.     protected final void  checkDaoConfig(){
  71.         如果 (!this .externalTemplate){
  72.             这个.sqlMapClientTemplate.afterPropertiesSet();
  73.         }
  74.     }
  75.     public  Map <String,SqlMapClient> getTargetSqlMapClients(){
  76.         返回 targetSqlMapClients;
  77.     }
  78.     public void  setTargetSqlMapClients(Map <String,SqlMapClient> targetSqlMapClients){
  79.         这个.targetSqlMapClients = targetSqlMapClients;
  80.     }
  81.     public  SqlMapClient getDefaultSqlMapClient(){
  82.         返回 defaultSqlMapClient;
  83.     }
  84.     public void  setDefaultSqlMapClient(SqlMapClient defaultSqlMapClient){
  85.         这个.defaultSqlMapClient = defaultSqlMapClient;
  86.     }
  87. }

IbatisDaoSupport.java

[java] 查看纯文本

  1.  com.ssi.dao;
  2. import  java.io.Serializable;
  3. import  java.sql.SQLException;
  4. import  java.util.List;
  5. import  java.util.Map;
  6. import  org.apache.commons.logging.Log;
  7. import  org.apache.commons.logging.LogFactory;
  8. import  org.springframework.orm.ibatis.SqlMapClientCallback;
  9. import  com.ibatis.sqlmap.client.SqlMapExecutor;
  10. @SuppressWarnings “未选中”
  11. 公共 IbatisDaoSupport <实体>  扩展 DynamicSqlClientDaoSupport  实现 IEntityDao <实体> {
  12.     受保护的最终 日志日志= LogFactory.getLog(getClass());
  13.     公共 实体get(String sqlId,Serializable id){
  14.         return  (Entity)getSqlMapClientTemplate()。queryForObject(sqlId,id);
  15.     }
  16.     公共 实体getByParamMap(字符串sqlId,对象参数){
  17.         return  (Entity)getSqlMapClientTemplate()。queryForObject(sqlId,param);
  18.     }
  19.     public  Object save(String sqlId,Object o){
  20.         返回 getSqlMapClientTemplate()。insert(sqlId,o);
  21.     }
  22.     public  Object batchSave(final  String sqlId,final  List <Entity> entityList)  throws  Exception {
  23.         //执行回调   
  24.         返回 getSqlMapClientTemplate()。execute(新的 SqlMapClientCallback(){
  25.             //实现回调接口   
  26.             公共 对象doInSqlMapClient(SqlMapExecutor执行器)  throws  SQLException {
  27.                 //开始批处理   
  28.                 executor.startBatch();
  29.                 for  (Entity entity:entityList){
  30.                     executor.insert(sqlId,entity);
  31.                 }
  32.                 return  executor.executeBatch();
  33.             }
  34.         });
  35.     }
  36.     public  Integer remove(String sqlId,Object o){
  37.         返回 getSqlMapClientTemplate()。delete(sqlId,o);
  38.     }
  39.     public  Integer removeById(String sqlId,Serializable id){
  40.         返回 getSqlMapClientTemplate()。delete(sqlId,id);
  41.     }
  42.     public  Integer update(String sqlId,Object o){
  43.         返回 getSqlMapClientTemplate()。update(sqlId,o);
  44.     }
  45.     public  long totalCount(String sqlId,Object o){
  46.         return  (Long)getSqlMapClientTemplate()。queryForObject(sqlId,o);
  47.     }
  48.     public  List <Entity> pagedList(String sqlId,Map <String,Object> map,int  pageSize,  int  pageNum){
  49.         int  start =(pageNum –  1 )* pageSize;
  50.         map.put(“开始” ,开始);
  51.         map.put(“pageSize” ,pageSize);
  52.         List <Entity> list = getSqlMapClientTemplate()。queryForList(sqlId,map);
  53.         退货 清单;
  54.     }
  55.     public  List <Entity> list(String sqlId,Object o){
  56.         返回 getSqlMapClientTemplate()。queryForList(sqlId,o);
  57.     }
  58.     public  List <Entity> list(String sqlId){
  59.         返回 getSqlMapClientTemplate()。queryForList(sqlId);
  60.     }
  61. }

 

UserDaoImpl.java:

[java] 查看纯文本

  1.  com.ssi.dao.impl;
  2. import  org.springframework.stereotype.Repository;
  3. import  com.ssi.dao.IUserDao;
  4. import  com.ssi.dao.IbatisDaoSupport;
  5. import  com.ssi.model.User;
  6. @Repository “userDAO的”
  7. 公共 UserDaoImpl  扩展 IbatisDaoSupport <用户>  实现 IUserDao {
  8.     公共 整数addUser(用户用户)  抛出 异常{
  9.         返回 (整数)  这个.save(“User.insert” ,用户);
  10.     }
  11. }

 

UserServiceImpl.java

[java] 查看纯文本

  1.  com.ssi.service.impl;
  2. import  javax.annotation.Resource;
  3. import  org.springframework.stereotype.Service;
  4. import  com.ssi.dao.IUserDao;
  5. import  com.ssi.datasource.DbContextHolder;
  6. import  com.ssi.model.User;
  7. import  com.ssi.service.IUserService;
  8. @Service “userService”
  9. 公共 UserServiceImpl  实现 IUserService {
  10.     @Resource private  IUserDao userDao;
  11.     / **
  12.      *测试在服务中切换数据源异常是否回滚
  13.      * /
  14.     公共无效 addUser(用户用户)  抛出 异常{
  15.         DbContextHolder.setDbType(“db1” );
  16.         userDao.addUser(用户);
  17.         DbContextHolder.setDbType(“db2” );
  18.         user.setUserName(“user2” );
  19.         userDao.addUser(用户);
  20.         DbContextHolder.setDbType(“center” );
  21.         user.setUserName(“user3” );
  22.         userDao.addUser(用户);
  23.         //System.out.println(1/0);
  24.     }
  25. }

 

DynamicDataSource.java:

[java] 查看纯文本

  1. public class  DynamicDataSource  extends  AbstractRoutingDataSource {
  2.     静态 记录仪日志= Logger.getLogger(DynamicDataSource。);
  3.     protected  Object determineCurrentLookupKey(){
  4.         返回 DbContextHolder.getDbType();
  5.     }
  6. }

DbContextHolder.java:

[java] 查看纯文本

  1. 公共 DbContextHolder {
  2.     private static final  ThreadLocal contextHolder =  new  ThreadLocal();
  3.     public static void  setDbType(String dbType){
  4.         contextHolder.set(DBTYPE);
  5.     }
  6.     public static  String getDbType(){
  7.         return  (String)contextHolder.get();
  8.     }
  9.     public static void  clearDbType(){
  10.         contextHolder.remove();
  11.     }
  12. }

三个数据库:dbcenter,db1,db2表结构均相同

脚本:

[sql] 查看纯文本

  1. DROP TABLE  IF EXISTS`tb_user`;
  2. CREATE TABLE  `tb_user`(
  3.   `id`  int (11)  NOT NULL  AUTO_INCREMENT,
  4.   `userName`  varchar (20)  DEFAULT NULL
  5.   ` 密码VARCHAR (60)  DEFAULT NULL
  6.   PRIMARY KEY  (`id`)
  7. )ENGINE = InnoDB  DEFAULT  CHARSET = utf8;

单元测试:

[java] 查看纯文本

  1. 公共 JunitTest {
  2.     public  ApplicationContext cxt;
  3.     @测试
  4.     公共无效的 init()  抛出 异常{
  5.         cxt =  new  ClassPathXmlApplicationContext(new  String [] { “applicationContext.xml” “applicationContext-datasource.xml” });
  6.         testInsertUser();
  7.     }
  8.     私人无效 testInsertUser()  抛出 异常{
  9.         IUserService userService =(IUserService)cxt.getBean(“userService” );
  10.         用户用户=   用户();
  11.         user.setUserName(“user1” );
  12.         user.setPassword(“0” );
  13.         userService.addUser(用户);
  14.     }
  15.     私人无效 testInsertUser2()  抛出 异常{
  16. }
未经允许不得转载:比尔云 » Spring分布式事务在service中动态切换数据源
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

强烈推荐

高性能SSD云服务器ECS抗攻击,高可用云数据库RDS