数据源 可自由切换 最好有demo参考 后期可能会加入 es 等
2条回答 默认 最新
- 大黄瓜de冬天 2021-05-24 11:18关注
第一步:手动清除线程变量副本,调用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; } }
基本就这些,事实上需要做的就是创建一个管理连接线程副本,用多数据源的信息测试连接,成功就初始化,在切换数据源,就可以在相应的数据源上进行表的操作。
解决 无用评论 打赏 举报
悬赏问题
- ¥50 微信聊天记录备份到电脑提示成功了,但还是没同步到电脑微信
- ¥15 python怎么在已有视频文件后添加新帧
- ¥20 虚幻UE引擎如何让多个同一个蓝图的NPC执行一样的动画,
- ¥15 fluent里模拟降膜反应的UDF编写
- ¥15 MYSQL 多表拼接link
- ¥15 关于某款2.13寸墨水屏的问题
- ¥15 obsidian的中文层级自动编号
- ¥15 同一个网口一个电脑连接有网,另一个电脑连接没网
- ¥15 神经网络模型一直不能上GPU
- ¥15 pyqt怎么把滑块和输入框相互绑定,求解决!