继承Spring AbstractRoutingDataSource实现路由切换
继承Spring AbstractRoutingDataSource实现路由切换
下面是结合项目整理的如何实现Spring下数据路由动态切换,分三部分,1.配置文件。2.java类。3.总结
一:配置文件
dataAnt.properties:
- driverClass1=oracle.jdbc.driver.OracleDriver
- jdbcUrl1=jdbc\:oracle\:thin\:@136.160.40.36\:1521\:crmtemp
- db.user1=crm_app
- db.password1=abc123
- driverClass2=oracle.jdbc.driver.OracleDriver
- jdbcUrl2=jdbc\:oracle\:thin\:@136.160.40.36\:1521\:crmtest
- db.user2=crm_app
- db.password2=abc123
sm-spring-db.xml:
- <!-- dataAnt默认数据源 -->
- <bean id="smDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="driverClassName" value="${driverClass1}">
- </property>
- <property name="url" value="${jdbcUrl1}">
- </property>
- <property name="username" value="${db.user1}">
- </property>
- <property name="password" value="${db.password1}">
- </property>
- <property name="accessToUnderlyingConnectionAllowed">
- <value>true</value>
- </property>
- </bean>
- <!-- 数据迁移目标数据源 -->
- <bean id="qyDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="driverClassName" value="${driverClass2}">
- </property>
- <property name="url" value="${jdbcUrl2}">
- </property>
- <property name="username" value="${db.user2}">
- </property>
- <property name="password" value="${db.password2}">
- </property>
- <property name="accessToUnderlyingConnectionAllowed">
- <value>true</value>
- </property>
- </bean>
- <!-- 数据路由dataSource-->
- <bean id="dataSource" class="com.ai.data.common.LinkageRoutingDataSource">
- <property name="targetDataSources">
- <map key-type="java.lang.String">
- <entry value-ref="smDataSource" key="smDataSource"></entry>
- <entry value-ref="qyDataSource" key="qyDataSource"></entry>
- </map>
- </property>
- <property name="defaultTargetDataSource" ref="smDataSource"></property> <!-- 默认使用ds1的数据源 -->
- </bean>
2.bean id = 'dataSource'里注意,必须设置目标数据源targetDataSource和defaultTargetDataSource。
注入jdbcTemplate工具类:
- <!--注入JdbcTemplate切换dataSource工具类[获取bean名,重设dataSource] -->
- <bean class="com.ai.data.common.JdbcTemplateUtil"></bean>
二:java类
LinkageRoutingDataSource.java
- package com.ai.data.common;
- import org.apache.commons.lang.StringUtils;
- import org.arrow.common.utils.Log;
- import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
- /**
- * 设置数据源
- *
- * @author weiweiai
- * @see [相关类/方法](可选)
- * @since [产品/模块版本] (可选)
- */
- public class LinkageRoutingDataSource extends AbstractRoutingDataSource {
- private static final Log log = Log.getLog(LinkageRoutingDataSource.class);
- //目标数据源
- private static final ThreadLocal<String> TARGET_DATA_SOURCE = new ThreadLocal<String>();
- //默认数据源--指标监控的
- public static final String DEFAULT_DATA_SOURCE = "smDataSource";
- /**
- * 根据PrvncUtil类设置进去的当前线程数据源进行数据源切换
- *
- * @param 无
- * @return 数据源名称
- */
- protected Object determineCurrentLookupKey() {
- String targetDataSource = TARGET_DATA_SOURCE.get();
- if (StringUtils.isEmpty(targetDataSource)) {
- targetDataSource = DEFAULT_DATA_SOURCE; //默认数据源为指标监控数据源
- TARGET_DATA_SOURCE.set(targetDataSource);
- }
- log.debug("当前线程数据源----------------:{}", targetDataSource);
- return targetDataSource;
- }
- /**
- * 设置数据源名
- * @param target
- */
- public static void setTargetDataSource(String target) {
- TARGET_DATA_SOURCE.set(target);
- }
- /**
- * 取数据源名
- * @return
- */
- public static String getTargetDataSource(){
- return TARGET_DATA_SOURCE.get();
- }
- }
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
- package com.ai.data.common;
- import org.apache.commons.lang.StringUtils;
- import org.arrow.common.utils.Log;
- /**
- * 设置获取当前线程数据源名称
- *
- * @date 20150924
- * @author weiweiai
- */
- public class PrvncUtil {
- private static final Log LOG_OUTPUT = Log.getLog(PrvncUtil.class);
- //默认数据源
- public static final String DEFAULT_DATA_SOURCE = "smDataSource";
- /**
- * 设置需要用的数据源名称
- *
- * @param dataSourceName 数据源名称
- * @retrun 无
- */
- public static void setDataSourceName(String dataSourceName) {
- LinkageRoutingDataSource.setTargetDataSource(getDataSourceName(dataSourceName));
- }
- /**
- * 获取数据源名称
- * @param dataSourceName
- * @return 数据源名称
- */
- public static String getDataSourceName(String dataSourceName) {
- String dataSource = dataSourceName;
- if(StringUtils.isEmpty(dataSource)){
- dataSource = DEFAULT_DATA_SOURCE;
- }
- LOG_OUTPUT.debug("最终获取到的当前数据源名称:{}", dataSource);
- //((JdbcTemplate)SpringContextHolder.getBean("jdbcTemplate")).setDataSource((DataSource)SpringContextHolder.getBean(dataSource));
- return dataSource;
- }
- }
JdbcTemplateUtil.java
- package com.ai.data.common;
- import javax.sql.DataSource;
- import org.springframework.beans.BeansException;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
- import org.springframework.jdbc.core.JdbcTemplate;
- import com.linkage.bss.commons.util.StringUtil;
- /**
- * JdbcTemplate工具类,实现切换数据源后JdbcTemplate数据源重设
- * @author weiweiai
- * 2016/5/10
- *
- */
- public class JdbcTemplateUtil implements ApplicationContextAware{
- @Autowired
- private JdbcTemplate jdbcTemplate;
- public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
- this.jdbcTemplate = jdbcTemplate;
- }
- private ApplicationContext ctx;
- /*
- * (non-Javadoc)
- * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
- */
- public void setApplicationContext(ApplicationContext applicationContext)
- throws BeansException {
- // TODO Auto-generated method stub
- this.ctx = applicationContext;
- }
- /*
- * 取LinkageRoutingDataSource 线程变量中数据源,重设jdbcTemplate的数据源属性
- */
- public JdbcTemplate getJdbcTemplate(){
- String ds = LinkageRoutingDataSource.getTargetDataSource();
- if(!StringUtil.isEmpty(ds))
- jdbcTemplate.setDataSource((DataSource)ctx.getBean(ds));
- return jdbcTemplate;
- }
- }
2.getJdbcTemplate()方法,先获取LinkageRoutingDataSource类中TARGET_DATA_SOURCE线程变量中保存的数据源名。
3.ds为空,返回的是默认数据源;否则切换jdbcTemplate的数据源。
调用:
- PrvncUtil.setDataSourceName("qyDataSource");
- primaryValueList = jdbcTemplateUtil.getJdbcTemplate().queryForList((String) dataMap.get("SELFORPRIMARY"));
三:总结
- PrvncUtil.setDataSourceName("qyDataSource");
primaryValueList = jdbcTemplateUtil.getJdbcTemplate().queryForList((String) dataMap.get("SELFORPRIMARY"));
实现了:
这里为什么jdbcTemplate已经指到dataSource.而dataSource也已经改变属性targetDataSource却没用,我也不懂,欢迎大神指导原因。我已经亲试,确实需要手工
- jdbcTemplate.setDataSource((DataSource)ctx.getBean(ds))