数据源 可自由切换 最好有demo参考 后期可能会加入 es 等
第一步:手动清除线程变量副本,调用clearDataSourceType方法
package com.cplink.framework.datasource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 数据源切换处理
*
* @author ruoyi
*/
public class DynamicDataSourceContextHolder
{
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源的变量
*/
public static void setDataSourceType(String dsType)
{
log.info("切换到{}数据源", dsType);
CONTEXT_HOLDER.set(dsType);
}
/**
* 获得数据源的变量
*/
public static String getDataSourceType()
{
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源变量
*/
public static void clearDataSourceType()
{
CONTEXT_HOLDER.remove();
}
}
第二步:准备数据源连接信息,包括:数据源名称(任取,不重名即可)、用户名、密码、url、数据库驱动(由数据库库类型代码转换,写mysql即可)
第三步:写一个继承AbstractRoutingDataSource的类,并连接数据源连接
package com.cplink.framework.datasource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.stat.DruidDataSourceStatManager;
import com.cplink.common.constant.Constants;
import com.cplink.common.utils.StringUtils;
import com.cplink.framework.config.properties.DruidProperties;
import com.cplink.framework.web.domain.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 动态数据源
*
* @author ruoyi
*/
public class DynamicDataSource extends AbstractRoutingDataSource
{
private final Log log = LogFactory.getLog(DynamicDataSource.class);
private Map<Object, Object> dynamicTargetDataSources;
@Autowired
private DruidProperties druidProperties;
public DynamicDataSource(javax.sql.DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
{
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey()
{
return DynamicDataSourceContextHolder.getDataSourceType();
}
@Override
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
super.setTargetDataSources(targetDataSources);
this.dynamicTargetDataSources = targetDataSources;
}
/**
* 检查并创建数据源(数据源存在不需要在创建)
* @param dataSource DataBaseSource实体类信息
* @param boo 是否默认数据目标源配置(true:系统默认连接信息,false自定义连接信息)
* @param targetDataSources 当boo为false时用此源扩展信息
* @throws Exception
*/
public void createDataSourceWithCheck(DataSource dataSource, boolean boo, DruidDataSource targetDataSources) throws Exception {
if(StringUtils.isNull(dataSource) || StringUtils.isEmpty(dataSource.getDataSourceId())){
log.info("数据源基本配置信息不能为空");
throw new Exception("数据源基本配置信息不能为空");
}
String datasourceId = dataSource.getDataSourceId();
log.info("正在检查数据源:"+datasourceId);
if(StringUtils.isEmpty(this.dynamicTargetDataSources))
this.dynamicTargetDataSources = new HashMap<Object, Object>();
Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
//不存在'datasourceId'数据源,新建数据源
if (!dynamicTargetDataSources2.containsKey(datasourceId)) {
createDataSource(dataSource,boo,targetDataSources);
return;
}
log.info("数据源"+datasourceId+"之前已经创建,准备测试数据源是否正常...");
DruidDataSource druidDataSource = (DruidDataSource) dynamicTargetDataSources2.get(datasourceId);
boolean rightFlag = true;
Connection connection = null;
try {
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("不需要重新创建数据源");
return;
}
log.info("准备重新创建数据源...");
createDataSource(dataSource,boo,targetDataSources);
log.info("重新创建数据源完成");
}
/**
* 创建数据源
* @param dataSource
* @throws Exception
*/
public void createDataSource(DataSource dataSource, boolean boo, DruidDataSource targetDataSources) throws Exception {
if(StringUtils.isNull(dataSource) || StringUtils.isEmpty(dataSource.getDataSourceId())){
log.info("数据源基本配置信息不能为空");
throw new Exception("数据源基本配置信息不能为空");
}
String datasourceId = dataSource.getDataSourceId();
log.info("准备创建数据源"+datasourceId);
testParamBoo(dataSource);
if(!testDatasource(dataSource.getUserName(),dataSource.getPassWord(),dataSource.getUrl(),dataSource.getDataBaseType())) {
log.error("数据源配置有错误");
throw new Exception("数据源配置有错误");
}
boolean result = this.initDataSource(dataSource,boo,targetDataSources);
if(!result) {
log.error("数据源"+datasourceId+"配置正确,但是创建失败");
throw new Exception("数据源"+datasourceId+"配置正确,但是创建失败");
}
}
/**
* 初始化数据源
* @param dataBaseSource
* @param boo 是否默认数据目标源配置(true:系统默认连接信息,false自定义连接信息)
* @param targetDataSources 当boo为false时此源必填
* @return
*/
public boolean initDataSource(DataSource dataBaseSource, boolean boo, DruidDataSource targetDataSources)throws Exception {
if(StringUtils.isNull(dataBaseSource) || StringUtils.isEmpty(dataBaseSource.getDataSourceId())){
log.info("数据源基本配置信息不能为空");
throw new Exception("数据源基本配置信息不能为空");
}
String key = dataBaseSource.getDataSourceId();
DruidDataSource druidDataSource = null;
if(!boo){
//自定义数据源信息
druidDataSource = getBaseDataSource(targetDataSources,dataBaseSource);
}else{
//默认数据源信息(除基本信息外,如用户名、密码、源名称、url连接信息、驱动)
DruidDataSource druidData = new DruidDataSource();
DruidDataSource dataSource = druidProperties.dataSource(druidData);
druidDataSource = getBaseDataSource(dataSource,dataBaseSource);
}
if(StringUtils.isNull(druidDataSource)){
log.error("数据源驱动配置错误");
return false;
}
try {
//数据源配置初始化
druidDataSource.init();
} catch (Exception e) {
log.error("数据源配置初始化错误:".concat(e + ""));
return false;
}
if(StringUtils.isEmpty(this.dynamicTargetDataSources))
this.dynamicTargetDataSources = new HashMap<Object, Object>();
this.dynamicTargetDataSources.put(key, druidDataSource);
setTargetDataSources(this.dynamicTargetDataSources);// 将map赋值给父类的TargetDataSources
super.afterPropertiesSet();// 将TargetDataSources中的连接信息放入resolvedDataSources管理
log.info(key.concat("数据源初始化成功"));
return true;
}
/**
* 删除数据源
* @param datasourceid
* @return
*/
public boolean delDatasources(String datasourceid) {
if(StringUtils.isEmpty(this.dynamicTargetDataSources))
this.dynamicTargetDataSources = new HashMap<Object, Object>();
Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
if (!dynamicTargetDataSources2.containsKey(datasourceid))
return false;
Set<DruidDataSource> druidDataSourceInstances = DruidDataSourceStatManager.getDruidDataSourceInstances();
for (DruidDataSource l : druidDataSourceInstances) {
if (datasourceid.equals(l.getName())) {
dynamicTargetDataSources2.remove(datasourceid);
DruidDataSourceStatManager.removeDataSource(l);
setTargetDataSources(dynamicTargetDataSources2);// 将map赋值给父类的TargetDataSources
super.afterPropertiesSet();// 将TargetDataSources中的连接信息放入resolvedDataSources管理
return true;
}
}
return false;
}
/**
* 测试数据源配置
* @param userName 用户名
* @param passWord 密码
* @param url url
* @param type 数据库类型
* @return
*/
public boolean testDatasource(String userName, String passWord, String url, String type){
String driveClass = getDriveClass(type);
if(Constants.FAIL.equals(driveClass))
return false;
try { // 排除连接不上的错误
Class.forName(driveClass);
DriverManager.getConnection(url, userName, passWord);// 相当于连接数据库
return true;
} catch (Exception e) {
return false;
}
}
/**
* 根据类型转换数据库驱动
* @param databasetype
* @return
*/
private String getDriveClass(String databasetype){
if("mysql".equalsIgnoreCase(databasetype))
return Constants.MYSQL_DRIVER;
if("oracle".equalsIgnoreCase(databasetype))
return Constants.ORACLE_DRIVER;
if("sqlserver".equalsIgnoreCase(databasetype))
return Constants.SQLSERVER_DRIVER;
return Constants.FAIL;
}
/**
* 封装数据源连接信息
* @param druidDataSource 自定义的连接信息或者默认的本系统连接信息
* @param dataSource 基本的数据源信息(包含数据驱动、数据源key、url连接信息、用户名、密码)
* @return
*/
private DruidDataSource getBaseDataSource(DruidDataSource druidDataSource, DataSource dataSource)throws Exception{
testParamBoo(dataSource);
druidDataSource.setName(dataSource.getDataSourceId());
String driveClass = getDriveClass(dataSource.getDataBaseType());
if(Constants.FAIL.equals(driveClass))
return null;
druidDataSource.setDriverClassName(driveClass);
druidDataSource.setUrl(dataSource.getUrl());
druidDataSource.setUsername(dataSource.getUserName());
druidDataSource.setPassword(dataSource.getPassWord());
return druidDataSource;
}
private void testParamBoo(DataSource dataSource)throws Exception{
if(StringUtils.isNull(dataSource) || StringUtils.isEmpty(dataSource.getDataSourceId())||StringUtils.isEmpty(dataSource.getDataBaseType())|| StringUtils.isEmpty(dataSource.getUserName())|| StringUtils.isEmpty(dataSource.getPassWord())|| StringUtils.isEmpty(dataSource.getUrl())){
log.info("数据源基本配置信息不能为空");
throw new Exception("数据源基本配置信息不能为空");
}
}
}
第五步:切换数据源
DynamicDataSourceContextHolder.clearDataSourceType();
String key = "test1";
String username = "test1";
String password = "test1";
String url = "";
String dataType = "mysql";
DataSource dataBaseSource = new DataSource();
dataBaseSource.setDataSourceId(key);
dataBaseSource.setUrl(url);
dataBaseSource.setDataBaseType(dataType);
dataBaseSource.setUserName(username);
dataBaseSource.setPassWord(password);
try{
//创建数据源连接&检查 若存在则不需重新创建
dynamicDataSource.createDataSourceWithCheck(dataBaseSource,true,null);
//切换到该数据源
DynamicDataSourceContextHolder.setDataSourceType(key);
} catch (Exception e) {
e.printStackTrace();
}
//下面写执行切换了数据源之后的表的操作
另外DruidProperties类
package com.cplink.framework.config.properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.pool.DruidDataSource;
/**
* druid 配置属性
*
* @author ruoyi
*/
@Configuration
public class DruidProperties
{
@Value("${spring.datasource.druid.initialSize}")
private int initialSize;
@Value("${spring.datasource.druid.minIdle}")
private int minIdle;
@Value("${spring.datasource.druid.maxActive}")
private int maxActive;
@Value("${spring.datasource.druid.maxWait}")
private int maxWait;
@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
private int maxEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.druid.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.druid.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.druid.testOnReturn}")
private boolean testOnReturn;
public DruidDataSource dataSource(DruidDataSource datasource)
{
/** 配置初始化大小、最小、最大 */
datasource.setInitialSize(initialSize);
datasource.setMaxActive(maxActive);
datasource.setMinIdle(minIdle);
/** 配置获取连接等待超时的时间 */
datasource.setMaxWait(maxWait);
/** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
/** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
/**
* 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
*/
datasource.setValidationQuery(validationQuery);
/** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */
datasource.setTestWhileIdle(testWhileIdle);
/** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
datasource.setTestOnBorrow(testOnBorrow);
/** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
datasource.setTestOnReturn(testOnReturn);
return datasource;
}
}
基本就这些,事实上需要做的就是创建一个管理连接线程副本,用多数据源的信息测试连接,成功就初始化,在切换数据源,就可以在相应的数据源上进行表的操作。
您好,我是有问必答小助手,您的问题已经有小伙伴解答了,您看下是否解决,可以追评进行沟通哦~
如果有您比较满意的答案 / 帮您提供解决思路的答案,可以点击【采纳】按钮,给回答的小伙伴一些鼓励哦~~
ps:问答VIP仅需29元,即可享受5次/月 有问必答服务,了解详情>>>https://vip.csdn.net/askvip?utm_source=1146287632