数据源连接池使用druid 其他的数据源基本原理相同
spring中配置默认数据源连接池如下:
<!-- 数据源配置, 使用 BoneCP 数据库连接池 -->
<bean id="dataSourceOne" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="name" value="dataSourceOne"/>
<!-- 数据源驱动类可不写,Druid默认会自动根据URL识别DriverClass -->
<property name="driverClassName" value="${jdbc.driver}" />
<!-- 基本属性 url、user、password -->
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${jdbc.pool.init}" />
<property name="minIdle" value="${jdbc.pool.minIdle}" />
<property name="maxActive" value="${jdbc.pool.maxActive}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="${jdbc.testSql}" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小(Oracle使用)
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> -->
<!-- 配置监控统计拦截的filters -->
<property name="filters" value="stat" />
</bean>
接下来配置多数据源bean
<!-- 多数据源配置 -->
<bean id="dynamicDataSource" class="com.XXX.datasource.DynamicDataSource" >
<property name="targetDataSources">
<map>
<entry value-ref="dataSourceOne" key="dataSourceOne"></entry>
<!--此处是对数据源的引用-->
<!-- <entry value-ref="dataSourceTow" key="dataSourceTow"></entry> -->
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSourceOne" />
<property name="debug" value="true"/>
</bean>
这个类 com.XXX.datasource.DynamicDataSource 需要手动创建
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.util.StringUtils;
import com.alibaba.druid.pool.DruidConnectionHolder;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.alibaba.druid.stat.DruidDataSourceStatManager;
import com.alibaba.druid.util.DruidDataSourceUtils;
/**
* @author zh
*/
public class DynamicDataSource extends AbstractRoutingDataSource{
private boolean debug = false;
Logger log = LoggerFactory.getLogger(this.getClass());
private Map<Object, Object> dynamicTargetDataSources;
private Object dynamicDefaultTargetDataSource;
/* (non-Javadoc)
@see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()
*/
@Override
protected Object determineCurrentLookupKey() {
String datasource=DBContextHolder.getDataSource();
if(debug)
{
if(StringUtils.isEmpty(datasource)){
log.info("---当前数据源:默认数据源---");
}else{
log.info("---当前数据源:"+datasource+"---");
}
}
return datasource;
}
@Override
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
super.setTargetDataSources(targetDataSources);
this.dynamicTargetDataSources = targetDataSources;
}
//创建数据源
public boolean createDataSource(String key,String driveClass,String url,String username,String password){
try {
try { //排除连接不上的错误
Class.forName(driveClass);
DriverManager.getConnection(url, username, password);
} catch (Exception e) {
return false;
}
@SuppressWarnings("resource")
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setName(key);
druidDataSource.setDriverClassName(driveClass);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
druidDataSource.setMaxWait(60000);
druidDataSource.setFilters("stat");
DataSource createDataSource = (DataSource)druidDataSource;
druidDataSource.init();
Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
dynamicTargetDataSources2.put(key, createDataSource);//加入map
setTargetDataSources(dynamicTargetDataSources2);//将map赋值给父类的TargetDataSources
super.afterPropertiesSet();//将TargetDataSources中的连接信息放入resolvedDataSources管理
return true;
} catch (Exception e) {
log.error(e+"");
return false;
}
}
//删除数据源
public boolean delDatasources(String datasourceid){
Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
if(dynamicTargetDataSources2.containsKey(datasourceid)){
Set<DruidDataSource> druidDataSourceInstances = DruidDataSourceStatManager.getDruidDataSourceInstances();
for(DruidDataSource l:druidDataSourceInstances){
if(datasourceid.equals(l.getName())){
System.out.println(l);
dynamicTargetDataSources2.remove(datasourceid);
DruidDataSourceStatManager.removeDataSource(l);
setTargetDataSources(dynamicTargetDataSources2);//将map赋值给父类的TargetDataSources
super.afterPropertiesSet();//将TargetDataSources中的连接信息放入resolvedDataSources管理
return true;
}
}
return false;
}else{
return false;
}
}
//测试数据源连接是否有效
public boolean testDatasource(String key,String driveClass,String url,String username,String password){
try {
Class.forName(driveClass);
DriverManager.getConnection(url, username, password);
return true;
} catch (Exception e) {
return false;
}
}
/**
* Specify the default target DataSource, if any.
* <p>The mapped value can either be a corresponding {@link javax.sql.DataSource}
* instance or a data source name String (to be resolved via a
* {@link #setDataSourceLookup DataSourceLookup}).
* <p>This DataSource will be used as target if none of the keyed
* {@link #setTargetDataSources targetDataSources} match the
* {@link #determineCurrentLookupKey()} current lookup key.
*/
public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
}
/**
* @param debug the debug to set
*/
public void setDebug(boolean debug) {
this.debug = debug;
}
/**
* @return the debug
*/
public boolean isDebug() {
return debug;
}
/**
* @return the dynamicTargetDataSources
*/
public Map<Object, Object> getDynamicTargetDataSources() {
return dynamicTargetDataSources;
}
/**
* @param dynamicTargetDataSources the dynamicTargetDataSources to set
*/
public void setDynamicTargetDataSources(
Map<Object, Object> dynamicTargetDataSources) {
this.dynamicTargetDataSources = dynamicTargetDataSources;
}
/**
* @return the dynamicDefaultTargetDataSource
*/
public Object getDynamicDefaultTargetDataSource() {
return dynamicDefaultTargetDataSource;
}
/**
* @param dynamicDefaultTargetDataSource the dynamicDefaultTargetDataSource to set
*/
public void setDynamicDefaultTargetDataSource(
Object dynamicDefaultTargetDataSource) {
this.dynamicDefaultTargetDataSource = dynamicDefaultTargetDataSource;
}
}
其中该类继承了spring的AbstractRoutingDataSource 查看其源码,发现所有的数据源都是通过
afterPropertiesSet() 将存放在targetDataSources 这个Map中的数据源赋值给resolvedDataSources
对象的,spring是从resolvedDataSources对象中获取数据源对象的,我们能需要把自己的数据源放入
resolvedDataSources这个Map中就ok了。
接下来创建数据源切换工具类
/**
* 数据源切换
* @author zh
*
*/
public class DBContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
//调用此方法,切换数据源
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
具体实动态新增数据源,需要创建数据库用以存储 数据库连接信息,以及数据源key信息。
初始话数据库连接数据源,可以使用spring监听 实现ApplicationListener即可,如下
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import com.casking.cdds.modules.datasource.entity.CNDatasources;
public class InitDatasourcesLS implements ApplicationListener<ApplicationEvent>{
@Autowired
private CNDatasourcesService service;
@Override
public void onApplicationEvent(ApplicationEvent event) {
List<CNDatasources> list = service.findList(new CNDatasources());
for(CNDatasources li:list){
//这里调用创建数据源的方法即可
service.addDataSourceDynamic(li.getDatasource(),li);
}
}
}