实现原理
1.扩展Spring的抽象类AbstractRoutingDataSource(该类充当了DataSource的路由中介,在运行时,能根据某key值来动态切换到真正的DataSource上),重写determineCurrentLookupkey()抽象方法。determineCurrentLookupkey()方法的返回值是我们所要用到的DataSource的key值,有了这个key值,我们就可以从map(该map的key和value值是在配置文件中配置好存入的)中取出对应的DataSource,如果找不到,就用默认数据源。
2.编写数据源操作类的接口,设置默认数据源为null,因为当程序没有找到关联的数据源时,就会调用默认数据源
package com.shadow.system.base.source;
import org.aspectj.lang.JoinPoint;
/**
* 数据源切换接口
*
* @author shadow
* @create 2013.04.03
*/
public interface DataSourceEntry {
// 默认数据源
public final static String DEFAULT_SOURCE = null;
/**
* 还原数据源
*
* @param joinPoint
*/
public void restore(JoinPoint join);
/**
* 设置数据源
*
* @param dataSource
*/
public void set(String source);
/**
* 获取数据源
*
* @return String
*/
public String get();
/**
* 清空数据源
*/
public void clear();
}
3.写个实现类,从当前线程取出数据源名
package com.shadow.system.base.source;
import org.aspectj.lang.JoinPoint;
import com.shadow.system.dictionary.DynamicTypeEntry;
/**
* 数据源切换实现类类
*
* @author shadow
* @create 2013.04.03
*/
public class DataSourceEntryImpl implements DynamicTypeEntry {
private final static ThreadLocal<String> local = new ThreadLocal<String>();
public void clear() {
local.remove();
}
public String get() {
return local.get();
}
public void restore(JoinPoint join) {
local.set(DEFAULT_SOURCE); // 设置null数据源
}
public void set(String source) {
local.set(source);
}
}
4.扩展AbstractRoutingDataSource类,并注入DataSourceEntry,重写里面的determineCurrentLookupkey()方法,实现动态切换数据源。
package com.shadow.system.base.source;
import javax.annotation.Resource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 获取数据源(依赖SPRING框架)
*
* @author shadow
* @create 2013.04.03
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private DataSourceEntry dataSourceEntry;
@Override
protected Object determineCurrentLookupKey() {
return this.dataSourceEntry.get();
}
@Resource
public void setDataSourceEntry(DataSourceEntry dataSourceEntry) {
this.dataSourceEntry = dataSourceEntry;
}
}
5.最后就是配置.xml文件,以后只需要直接管理DynamicDataSource这个接口就可以了,至于它内部是使用的是哪个数据源是不需要关注的。写的切面还原是为了保证每次调用完另外的数据源都会还原成默认数据源,防止有的人忘记设置回默认的,导致其他代码出问题。项目中,为了精简,可以单独分离出来jdbc.xml数据源配置文件。
<!-- JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dynamicDataSource" />
</bean>
<!-- 获取数据源配置 -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:properties/jdbc.properties</value>
</property>
</bean>
<!-- 配置动态数据源 -->
<bean id="dynamicDataSource" class="com.shadow.system.base.source.DynamicDataSource">
<!-- 通过key-value的形式来关联数据源 -->
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="C3P0_COMMON" key="C3P0_COMMON"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="C3P0_COMMON" />
</bean>
<!-- database config -->
<bean id="C3P0_COMMON" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close" lazy-init="true">
<property name="driverClass" value="${sqlite.driverClass}" />
<property name="jdbcUrl" value="${sqlite.jdbcUrl}" />
<property name="minPoolSize" value="${sqlite.miniPoolSize}" />
<property name="maxPoolSize" value="${sqlite.maxPoolSize}" />
<property name="initialPoolSize" value="${sqlite.initialPoolSize}" />
<property name="maxIdleTime" value="${sqlite.maxIdleTime}" />
<property name="acquireIncrement" value="${sqlite.acquireIncrement}" />
<property name="acquireRetryAttempts" value="${sqlite.acquireRetryAttempts}" />
<property name="acquireRetryDelay" value="${sqlite.acquireRetryDelay}" />
<property name="testConnectionOnCheckin" value="${sqlite.testConnectionOnCheckin}" />
<property name="testConnectionOnCheckout" value="${sqlite.testConnectionOnCheckout}" />
<property name="autoCommitOnClose" value="${sqlite.autoCommitOnClose}" />
<property name="idleConnectionTestPeriod" value="${sqlite.idleConnectionTestPeriod}" />
<property name="checkoutTimeout" value="${sqlite.checkoutTimeout}" />
<property name="numHelperThreads" value="${sqlite.numHelperThreads}" />
</bean>
<!-- 配置数据源切换实现类 -->
<bean id="dataSourceEntry" class="com.shadow.system.base.source.DataSourceEntryImpl" />
<!-- 切面还原默认数据源 -->
<aop:config>
<aop:aspect id="dataSourceHolderAdviceAspect" ref="dataSourceEntry">
<aop:after method="restore"
pointcut=<span><span class="string">"execution(* com.shadow.mvc.service.SmsService.saveOrderSMS(..))"</span><span></span></span> />
</aop:aspect>
</aop:config>
6.在程序中如何变换数据源,可以在切面中通过before,来检测切换数据源的方法。也可以在程序中直接使用DataSourceEntry的set方法。
public List<Sms> findForAll() {
dataSourceEntry.set(DynamicTypeEntry.C3P0_ACCESS);
return this.smsDao.queryForAll();
}