继承Spring AbstractRoutingDataSource实现路由切换

继承Spring AbstractRoutingDataSource实现路由切换

原创 2016年05月11日 16:50:08

下面是结合项目整理的如何实现Spring下数据路由动态切换,分三部分,1.配置文件。2.java类。3.总结

一:配置文件

dataAnt.properties:

[html] view plain copy
 
  1. driverClass1=oracle.jdbc.driver.OracleDriver  
  2. jdbcUrl1=jdbc:oracle:thin:@136.160.40.36:1521:crmtemp  
  3. db.user1=crm_app  
  4. db.password1=abc123  
  5.   
  6. driverClass2=oracle.jdbc.driver.OracleDriver  
  7. jdbcUrl2=jdbc:oracle:thin:@136.160.40.36:1521:crmtest  
  8. db.user2=crm_app  
  9. db.password2=abc123  
配置文件,两个jdbc的配置

sm-spring-db.xml:

[html] view plain copy
 
  1. <!-- dataAnt默认数据源 -->  
  2. <bean id="smDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
  3.     <property name="driverClassName" value="${driverClass1}">  
  4.     </property>  
  5.     <property name="url" value="${jdbcUrl1}">  
  6.     </property>  
  7.     <property name="username" value="${db.user1}">  
  8.     </property>  
  9.     <property name="password" value="${db.password1}">  
  10.     </property>  
  11.     <property name="accessToUnderlyingConnectionAllowed">  
  12.         <value>true</value>  
  13.     </property>  
  14. </bean>  
  15.   
  16. <!-- 数据迁移目标数据源 -->  
  17. <bean id="qyDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
  18.     <property name="driverClassName" value="${driverClass2}">  
  19.     </property>  
  20.     <property name="url" value="${jdbcUrl2}">  
  21.     </property>  
  22.     <property name="username" value="${db.user2}">  
  23.     </property>  
  24.     <property name="password" value="${db.password2}">  
  25.     </property>  
  26.     <property name="accessToUnderlyingConnectionAllowed">  
  27.         <value>true</value>  
  28.     </property>  
  29. </bean>  
  30.   
  31.   
  32. <!-- 数据路由dataSource-->  
  33. <bean id="dataSource" class="com.ai.data.common.LinkageRoutingDataSource">  
  34.     <property name="targetDataSources">  
  35.         <map key-type="java.lang.String">  
  36.             <entry value-ref="smDataSource" key="smDataSource"></entry>  
  37.             <entry value-ref="qyDataSource" key="qyDataSource"></entry>  
  38.         </map>  
  39.     </property>  
  40.     <property name="defaultTargetDataSource" ref="smDataSource"></property>      <!-- 默认使用ds1的数据源 -->  
  41. </bean>  
1.这里,配置了两个数据源smDataSource,qyDataSource,并且通过类LinkageRoutingDataSource类实现路由切换。注意设置Bean id为dataSource,保证后面事物控制到对应数据源。

2.bean id = 'dataSource'里注意,必须设置目标数据源targetDataSource和defaultTargetDataSource。


注入jdbcTemplate工具类:

[html] view plain copy
 
  1. <!--注入JdbcTemplate切换dataSource工具类[获取bean名,重设dataSource] -->  
  2.     <bean class="com.ai.data.common.JdbcTemplateUtil"></bean>  


二:java类

LinkageRoutingDataSource.java

[java] view plain copy
 
  1. package com.ai.data.common;  
  2.   
  3. import org.apache.commons.lang.StringUtils;  
  4. import org.arrow.common.utils.Log;  
  5. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
  6.   
  7. /** 
  8.  * 设置数据源 
  9.  * 
  10.  * @author weiweiai 
  11.  * @see [相关类/方法](可选) 
  12.  * @since [产品/模块版本] (可选) 
  13.  */  
  14. public class LinkageRoutingDataSource extends AbstractRoutingDataSource {   
  15.     private static final Log log = Log.getLog(LinkageRoutingDataSource.class);  
  16.       
  17.     //目标数据源  
  18.     private static final ThreadLocal<String> TARGET_DATA_SOURCE = new ThreadLocal<String>();  
  19.     //默认数据源--指标监控的  
  20.     public static final String DEFAULT_DATA_SOURCE = "smDataSource";  
  21.       
  22.     /** 
  23.      * 根据PrvncUtil类设置进去的当前线程数据源进行数据源切换 
  24.      *  
  25.      * @param 无 
  26.      * @return 数据源名称 
  27.      */  
  28.     protected Object determineCurrentLookupKey() {  
  29.         String targetDataSource = TARGET_DATA_SOURCE.get();  
  30.         if (StringUtils.isEmpty(targetDataSource)) {  
  31.             targetDataSource = DEFAULT_DATA_SOURCE; //默认数据源为指标监控数据源  
  32.             TARGET_DATA_SOURCE.set(targetDataSource);  
  33.         }  
  34.         log.debug("当前线程数据源----------------:{}", targetDataSource);  
  35.         return targetDataSource;  
  36.     }    
  37.       
  38.     /** 
  39.      * 设置数据源名 
  40.      * @param target 
  41.      */  
  42.     public static void setTargetDataSource(String target) {  
  43.         TARGET_DATA_SOURCE.set(target);  
  44.     }  
  45.       
  46.     /** 
  47.      * 取数据源名 
  48.      * @return 
  49.      */  
  50.     public static String getTargetDataSource(){  
  51.         return TARGET_DATA_SOURCE.get();  
  52.     }  
  53.       
  54.       
  55. }  

1.实现Spring提供的AbstractRoutingDataSource抽象类,利用ThreadLocal类型来定义TARGET_DATA_SOURCE(目标数据源),保证线程间数据源名不互相影响。

2.指定默认数据源名,对应第一步中bean id =‘smDataSource’

3.determineCurrentLookupKey这个方法,个人理解,如下图:意思应该是说code想建立数据源连接时候,此方法就会执行目的是找到合适数据源。

这里代码里,就是先取TARGET_DATA_SOURCE线程变量里线程安全的TARGET_DATA_SOURCE,取不到就用默认数据源smDataSource。从而实现bean id='dataSource'属性targetDataSources切换。

PrvncUtil.java

[java] view plain copy
 
  1. package com.ai.data.common;  
  2.   
  3. import org.apache.commons.lang.StringUtils;  
  4. import org.arrow.common.utils.Log;  
  5.   
  6. /** 
  7.  * 设置获取当前线程数据源名称 
  8.  *  
  9.  * @date 20150924 
  10.  * @author weiweiai 
  11.  */  
  12. public class PrvncUtil {  
  13.       
  14.     private static final Log LOG_OUTPUT = Log.getLog(PrvncUtil.class);  
  15.     
  16.     //默认数据源  
  17.     public static final String DEFAULT_DATA_SOURCE = "smDataSource";  
  18.       
  19.     /** 
  20.      * 设置需要用的数据源名称 
  21.      *  
  22.      * @param dataSourceName 数据源名称 
  23.      * @retrun 无 
  24.      */  
  25.     public static void setDataSourceName(String dataSourceName) {    
  26.         LinkageRoutingDataSource.setTargetDataSource(getDataSourceName(dataSourceName));  
  27.     }    
  28.       
  29.     /** 
  30.      * 获取数据源名称 
  31.      * @param dataSourceName 
  32.      * @return 数据源名称 
  33.      */  
  34.     public static String getDataSourceName(String dataSourceName) {  
  35.         String dataSource = dataSourceName;  
  36.         if(StringUtils.isEmpty(dataSource)){  
  37.             dataSource = DEFAULT_DATA_SOURCE;  
  38.         }  
  39.         LOG_OUTPUT.debug("最终获取到的当前数据源名称:{}", dataSource);  
  40.         //((JdbcTemplate)SpringContextHolder.getBean("jdbcTemplate")).setDataSource((DataSource)SpringContextHolder.getBean(dataSource));  
  41.         return dataSource;    
  42.     }    
  43. }    
数据路由切换工具类

JdbcTemplateUtil.java

[java] view plain copy
 
  1. package com.ai.data.common;  
  2.   
  3. import javax.sql.DataSource;  
  4.   
  5. import org.springframework.beans.BeansException;  
  6. import org.springframework.beans.factory.annotation.Autowired;  
  7. import org.springframework.context.ApplicationContext;  
  8. import org.springframework.context.ApplicationContextAware;  
  9. import org.springframework.jdbc.core.JdbcTemplate;  
  10. import com.linkage.bss.commons.util.StringUtil;  
  11.   
  12.   
  13. /** 
  14.  * JdbcTemplate工具类,实现切换数据源后JdbcTemplate数据源重设 
  15.  * @author weiweiai 
  16.  * 2016/5/10 
  17.  * 
  18.  */  
  19. public class JdbcTemplateUtil implements ApplicationContextAware{  
  20.   
  21.     @Autowired  
  22.     private JdbcTemplate jdbcTemplate;  
  23.       
  24.     public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {  
  25.         this.jdbcTemplate = jdbcTemplate;  
  26.     }  
  27.   
  28.     private ApplicationContext ctx;  
  29.       
  30.     /* 
  31.      * (non-Javadoc) 
  32.      * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) 
  33.      */  
  34.     public void setApplicationContext(ApplicationContext applicationContext)  
  35.             throws BeansException {  
  36.         // TODO Auto-generated method stub  
  37.         this.ctx = applicationContext;  
  38.     }  
  39.       
  40.     /* 
  41.      * 取LinkageRoutingDataSource 线程变量中数据源,重设jdbcTemplate的数据源属性 
  42.      */  
  43.     public JdbcTemplate getJdbcTemplate(){  
  44.         String ds = LinkageRoutingDataSource.getTargetDataSource();  
  45.         if(!StringUtil.isEmpty(ds))  
  46.             jdbcTemplate.setDataSource((DataSource)ctx.getBean(ds));  
  47.         return jdbcTemplate;  
  48.     }  
  49. }  
1.实现ApplicationContextAware接口,且在第一步中已引入,作用是获取到Spring容器中注入的bean。

2.getJdbcTemplate()方法,先获取LinkageRoutingDataSource类中TARGET_DATA_SOURCE线程变量中保存的数据源名。

3.ds为空,返回的是默认数据源;否则切换jdbcTemplate的数据源。


调用:

[java] view plain copy
 
  1. PrvncUtil.setDataSourceName("qyDataSource");  
  2. primaryValueList = jdbcTemplateUtil.getJdbcTemplate().queryForList((String) dataMap.get("SELFORPRIMARY"));  



三:总结

[html] view plain copy
 
  1. PrvncUtil.setDataSourceName("qyDataSource");  
实现了切换:
 


primaryValueList = jdbcTemplateUtil.getJdbcTemplate().queryForList((String) dataMap.get("SELFORPRIMARY"));

实现了:

这里为什么jdbcTemplate已经指到dataSource.而dataSource也已经改变属性targetDataSource却没用,我也不懂,欢迎大神指导原因。我已经亲试,确实需要手工

[html] view plain copy
 
  1. jdbcTemplate.setDataSource((DataSource)ctx.getBean(ds))  
不然数据源切不过来,已验证。
原文地址:https://www.cnblogs.com/diegodu/p/8067231.html