项目中遇到需要动态切换数据库的需求,在特定的界面需要去另外一个数据库查询信息
直接上代码
1.新建DataSourceType .java(配置数据库名称)
package com.grand.datasourse;
/**
* 数据库名称 -需要合配置文件里的名称一致
* @author
*
* @time 2017年10月23日
*/
public class DataSourceType {
public static final String SOURCE_UWP = "uwp_dbsourse";//主数据库
public static final String SOURCE_WD = "wd_dbsourse";//从数据库
}
2.新建DataSourceContextHolder .java 切换数据库的类
package com.grand.datasourse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author 数据库切换的工具类:切换数据库
*
* @time 2017年10月22日
*/
public class DataSourceContextHolder {
//线程局部变量
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
private static final Logger LOG = LoggerFactory
.getLogger(DataSourceContextHolder.class);
/**
* 提供给AOP去设置当前的线程的数据源的信息
* 切换数据库
* @param dbType 数据库名-配置文件中一致
*/
public static void setDbType(String dbType) {
try {
contextHolder.set(dbType);
} catch (Exception e) {
// TODO: handle exception
LOG.debug("ERROR____DB_Exception");
LOG.debug(e.getMessage());
}
}
/**
* 提供给AbstractRoutingDataSource的实现类,通过key选择数据源
* @return java.lang.String
*/
public static String getDbType() {
String dataSourse = contextHolder.get();
if(null == dataSourse){
DataSourceContextHolder.setDbType(DataSourceType.SOURCE_UWP);
}
return contextHolder.get();
}
/**
* 使用默认的数据源
*/
public static void clearDbType() {
contextHolder.remove();
}
}
3.新建DynamicDataSource.java 数据源配置
package com.grand.datasourse;
import java.util.logging.Logger;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* descrption: 多数据源的选择
* @author
* AbstractRoutingDataSource中的抽象方法determineCurrentLookupKey是实现数据源的route的核心
* .需要重写该方法
* @time 2017年10月22日
*/
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
public Logger getParentLogger() {
return null;
}
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDbType();//获取数据源
}
}
4.applicationContext.xml 里配置数据源
<!--主库-->
<bean id ="uwp_dbsourse" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName2}" />
<property name="jdbcUrl" value="${jdbc.url2}" />
<property name="user" value="${jdbc.userDS.username2}" />
<property name="password" value="${jdbc.userDS.password2}" />
<!-- 连接池中保留的最小连接数 -->
<property name="minPoolSize" value="1" />
<!-- 连接池中保留的最大连接数 -->
<property name="maxPoolSize" value="200" />
<!-- 最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0-->
<property name="maxIdleTime" value="1800" />
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3-->
<property name="acquireIncrement" value="5" />
<!-- 最大的PreparedStatement的数量 -->
<property name="maxStatements" value="1000" />
<!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod" value="60" />
<!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30-->
<property name="acquireRetryAttempts" value="30" />
<property name="breakAfterAcquireFailure" value="true" />
<property name="testConnectionOnCheckout" value="false" />
</bean >
<!--从库-->
<bean id ="wd_dbsourse" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName1}" />
<property name="jdbcUrl" value="${jdbc.url1}" />
<property name="user" value="${jdbc.userDS.username1}" />
<property name="password" value="${jdbc.userDS.password1}" />
<!-- 连接池中保留的最小连接数 -->
<property name="minPoolSize" value="1" />
<!-- 连接池中保留的最大连接数 -->
<property name="maxPoolSize" value="200" />
<!-- 最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0-->
<property name="maxIdleTime" value="1800" />
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3-->
<property name="acquireIncrement" value="5" />
<!-- 最大的PreparedStatement的数量 -->
<property name="maxStatements" value="1000" />
<!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod" value="60" />
<!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30-->
<property name="acquireRetryAttempts" value="30" />
<property name="breakAfterAcquireFailure" value="true" />
<property name="testConnectionOnCheckout" value="false" />
</bean >
<!-- 动态配置数据源 -->
<bean id ="dataSource" class= "com.grand.datasourse.DynamicDataSource" >
<property name ="targetDataSources">
<map key-type ="java.lang.String">
<entry value-ref ="wd_dbsourse" key= "wd_dbsourse"></entry >
<entry value-ref ="uwp_dbsourse" key= "uwp_dbsourse"></entry >
</map >
</property >
<property name ="defaultTargetDataSource" ref= "uwp_dbsourse"></property > <!-- 默认使用的数据源 -->
</bean >
5.service 层调用
public List<Test> getTest(){
List<Test> list = null;
try {
// 切换数据库
DataSourceContextHolder.setDbType(DataSourceType.SOURCE_WD);
list = wdFundMapper.getTest();
} catch (Exception e) {
e.printStackTrace();
LOG.error(e.getMessage());
}
DataSourceContextHolder.clearDbType();//切回主库
return list;
}
注意:项目中发现,这种service层调用方式,存在小瑕疵,在同一个service 中,只能调用一个库,无法进行切库操作
例如:A service中,调用主库
B service中,掉用从库
A 中调用B的方法,往往失败
本人的做法是,将调用从库的方法统一写在B中,在控制层同时调用AB方法,将数据在控制层做区分