动态数据源2
package com.ysw.drivers.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.stat.DruidDataSourceStatManager;
import com.ysw.drivers.pojo.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.util.StringUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Map;
import java.util.Set;
/**
* @author ysw
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private boolean debug = true;
private final Logger log = LoggerFactory.getLogger(getClass());
private Map<Object, Object> dynamicTargetDataSources;
private Object dynamicDefaultTargetDataSource;
@Override
protected Object determineCurrentLookupKey() {
String dataSource = DBContextHolder.getDataSource();
if (!StringUtils.isEmpty(dataSource)) {
Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
if (dynamicTargetDataSources2.containsKey(dataSource)) {
log.info("-----当前数据源: " + dataSource + "--------------");
} else {
log.info("不存在的数据源:");
return null;
}
} else {
log.info("-------当前数据源: 默认数据源-------");
}
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, String databasetype) {
try {
try {
Class.forName(driveClass);
//相当于连接数据库
DriverManager.getConnection(url, username, password);
} catch (Exception e) {
return false;
}
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setName(key);
druidDataSource.setDriverClassName(driveClass);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
//初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
druidDataSource.setInitialSize(1);
//最大连接池数量
druidDataSource.setMaxActive(20);
//获取连接时最大等待时间,单位毫秒。当链接数已经达到了最大链接数的时候,应用如果还要获取链接就会出现等待的现象,等待链接释放并回到链接池,如果等待的时间过长就应该踢掉这个等待,不然应用很可能出现雪崩现象
druidDataSource.setMaxWait(60000);
//最小连接池数量
druidDataSource.setMinIdle(5);
String validationQuery = "select 1 from dual";
//申请连接时执行validationQuery检测连接是否有效,这里建议配置为TRUE,防止取到的连接不可用
druidDataSource.setTestOnBorrow(true);
//建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
druidDataSource.setTestWhileIdle(true);
//用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
druidDataSource.setValidationQuery(validationQuery);
//属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
druidDataSource.setFilters("stat");
//配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
//配置一个连接在池中最小生存的时间,单位是毫秒,这里配置为3分钟180000
druidDataSource.setMinEvictableIdleTimeMillis(180000);
//打开druid.keepAlive之后,当连接池空闲时,池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作,即执行druid.validationQuery指定的查询SQL,一般为select * from dual,只要minEvictableIdleTimeMillis设置的小于防火墙切断连接时间,就可以保证当连接空闲时自动做保活检测,不会被防火墙切断
druidDataSource.setKeepAlive(true);
//是否移除泄露的连接/超过时间限制是否回收。
druidDataSource.setRemoveAbandoned(true);
//泄露连接的定义时间(要超过最大事务的处理时间);单位为秒。这里配置为1小时
druidDataSource.setRemoveAbandonedTimeout(3600);
//移除泄露连接发生是是否记录日志
druidDataSource.setLogAbandoned(true);
druidDataSource.init();
this.dynamicTargetDataSources.put(key, druidDataSource);
// 将map赋值给父类的TargetDataSources
setTargetDataSources(this.dynamicTargetDataSources);
// 将TargetDataSources中的连接信息放入resolvedDataSources管理
super.afterPropertiesSet();
log.info(key + "数据源初始化成功");
//log.info(key+"数据源的概况:"+druidDataSource.dump());
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 druidDataSource : druidDataSourceInstances) {
if (dataSourceId.equals(druidDataSource.getName())) {
dynamicTargetDataSources2.remove(dataSourceId);
DruidDataSourceStatManager.removeDataSource(druidDataSource);
// 将map赋值给父类的TargetDataSources
setTargetDataSources(dynamicTargetDataSources2);
// 将TargetDataSources中的连接信息放入resolvedDataSources管理
super.afterPropertiesSet();
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;
}
}
@Override
public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
public boolean isDebug() {
return debug;
}
public Map<Object, Object> getDynamicTargetDataSources() {
return dynamicTargetDataSources;
}
public void setDynamicTargetDataSources(Map<Object, Object> dynamicTargetDataSources) {
this.dynamicTargetDataSources = dynamicTargetDataSources;
}
public Object getDynamicDefaultTargetDataSource() {
return dynamicDefaultTargetDataSource;
}
public void setDynamicDefaultTargetDataSource(Object dynamicDefaultTargetDataSource) {
this.dynamicDefaultTargetDataSource = dynamicDefaultTargetDataSource;
}
public void createDataSourceWithCheck(DataSource dataSource) throws Exception {
String datasourceId = dataSource.getDatasourceId();
log.info("正在检查数据源: {}", datasourceId);
Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
if (dynamicTargetDataSources2.containsKey(datasourceId)) {
log.info("数据源: {} 之前己经创建,准备测试数据源是否正常....", datasourceId);
DruidDataSource druidDataSource = (DruidDataSource) dynamicTargetDataSources2.get(datasourceId);
boolean rightFlag = true;
Connection connection = null;
try {
log.info("{} 数据源的概况--->当前闲置连接数:{} ", datasourceId, druidDataSource.getPoolingCount());
long activeCount = druidDataSource.getActiveCount();
log.info("{} 数据源的概况--->当前活动连接数:{} ", datasourceId, druidDataSource.getActiveCount());
if (activeCount > 0) {
log.info("{} 数据源的概况--->当前连接堆栈信息:{} ", datasourceId, druidDataSource.getActiveConnectionStackTrace());
}
log.info("准备获取数据库连接...");
connection = druidDataSource.getConnection();
log.info("数据源: {} 正常", datasourceId);
} catch (Exception e) {
//把异常信息打印到日志文件
log.error(e.getMessage(), e);
rightFlag = false;
log.info("缓存数据源: {}已失效,准备删除...", datasourceId);
if (delDataSources(datasourceId)) {
log.info("缓存数据源删除成功");
} else {
log.info("缓存数据源删除失败");
}
} finally {
if (null != connection) {
connection.close();
}
}
if (rightFlag) {
log.info("不需要重新创建数据源");
} else {
log.info("准备重新创建数据源....");
createDataSource(dataSource);
}
} else {
createDataSource(dataSource);
}
}
private void createDataSource(DataSource dataSource) {
String datasourceId = dataSource.getDatasourceId();
log.info("准备创建数据源 :{}", dataSource);
String databasetype = dataSource.getDatabasetype();
String userName = dataSource.getUserName();
String passWord = dataSource.getPassWord();
String url = dataSource.getUrl();
String driverClass = "com.mysql.cj.jdbc.Driver";
if (testDatasource(datasourceId, driverClass, url, userName, passWord)) {
boolean result = this.createDataSource(datasourceId, driverClass, url, userName, passWord, databasetype);
if (!result) {
log.error("数据源 :{} 配置正确,但是创建失败", datasourceId);
// throw new ADIException("数据源"+datasourceId+"配置正确,但是创建失败",500);
}
} else {
log.error("数据源配置有错误");
// throw new ADIException("数据源配置有错误",500);
}
}
}