下面是结合项目整理的如何实现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
配置文件,两个jdbc的配置
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>
1.这里,配置了两个数据源smDataSource,qyDataSource,并且通过类LinkageRoutingDataSource类实现路由切换。注意设置Bean id为dataSource,保证后面事物控制到对应数据源。
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;
}
}
1.实现ApplicationContextAware接口,且在第一步中已引入,作用是获取到Spring容器中注入的bean。
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))
不然数据源切不过来,已验证。