当前位置: 首页 > news >正文

springboot动态使用DruidDataSource切换数据源(动态配置多个数据源)

1、添加依赖,在pom文件中添加

 <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>2.5.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency>

2、添加数据源名字定义,这个可有可无,没有的话,需要在切面和运用中定义

public class DataSourceConstants {/*** master数据源*/public static final String DS_KEY_MASTER = "master";/*** slave数据源*/public static final String DS_KEY_SLAVE = "slave";
}

3、添加注解类

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 标记注解可使用在方法与类上
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DS {// 默认值为MASTERString value() default DataSourceConstants.DS_KEY_MASTER;
}

4、动态数据源的切换定义

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/*** 动态数据源**/
public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getContextKey();}
}

5、动态切面实现类

import cn.com.yixiukeji.app.ServiceContext;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;/*** 切面*/
@Aspect
@Component
//@Order(-10)
@ConditionalOnProperty(prefix = "yixiu.cube.center.multi-datasource",value = {"enable"},havingValue = "true"
)
public class DynamicDataSourceAspect {// 以在类上使用了@Service作为切入点@Pointcut("@within(org.springframework.stereotype.Service)")public void dataSourcePointCut() {}@Around("dataSourcePointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Class<?> aClass = Class.forName(signature.getDeclaringType().getName());// 方法优先,如果方法上存在注解,则优先使用方法上的注解if (signature.getMethod().isAnnotationPresent(DS.class)) { DynamicDataSourceContextHolder.setContextKey(signature.getMethod().getAnnotation(DS.class).value());// 其次类优先,如果类上存在注解,则使用类上的注解}else  if (aClass.isAnnotationPresent(DS.class)) { DynamicDataSourceContextHolder.setContextKey(aClass.getAnnotation(DS.class).value());// 如果都不存在,则使用默认}   else {//默认选择源DynamicDataSourceContextHolder.setContextKey("slave");}System.out.println(ServiceContext.getContext().get("yes.req.instanceId"));try {return joinPoint.proceed();} finally {DynamicDataSourceContextHolder.removeContextKey();}}
}

6、动态数据源配置

/*** 动态数据源配置**/
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
@Configuration
@ConditionalOnProperty(prefix = "yixiu.cube.center.multi-datasource",value = {"enable"},havingValue = "true"
)
@MapperScan(basePackages = "cn.com.dao.mapper")
public class DynamicDataSourceConfig {@Bean(DataSourceConstants.DS_KEY_MASTER)// 需要与配置文件中对应@ConfigurationProperties(prefix = "yixiu.cube.center.multi-datasource.datasource.master")public DruidDataSource masterDataSource() {return DruidDataSourceBuilder.create().build();}@Bean(DataSourceConstants.DS_KEY_SLAVE)@ConfigurationProperties(prefix = "yixiu.cube.center.multi-datasource.datasource.slave")public DruidDataSource slaveDataSource() {return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "yixiu.cube.center.multi-datasource")@ConditionalOnMissingBeanpublic MultiDataSourceProperties multiDatasourceProperties() {return new MultiDataSourceProperties();}@Bean@ConditionalOnMissingBeanpublic DataSource dataSource(MultiDataSourceProperties multiDatasourceProperties) {DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();multiDatasourceProperties.getDatasource().forEach((name, dataSourceVo) -> {DataSource data = DataSourceBuilder.create().url(dataSourceVo.getUrl()).username(dataSourceVo.getUsername()).password(dataSourceVo.getPassword()).driverClassName(dataSourceVo.getDriverClassName()).build();dataSource.addDataSource(name,data);});return dataSource;}@Bean@Primarypublic DynamicDataSource dynamicDataSource(MultiDataSourceProperties multiDataSourceProperties) {Map<Object, Object> dataSourceMap = new ConcurrentHashMap<>(multiDataSourceProperties.getDatasource().size());DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();multiDataSourceProperties.getDatasource().forEach((name, dataSourceVo) -> {DataSource data = DataSourceBuilder.create().url(dataSourceVo.getUrl()).username(dataSourceVo.getUsername()).password(dataSourceVo.getPassword()).driverClassName(dataSourceVo.getDriverClassName()).build();dataSource.addDataSource(name,data);dataSourceMap.put(name,data);});
//这种配置是定死的,不好玩
//        dataSourceMap.put(DataSourceConstants.DS_KEY_MASTER, masterDataSource());
//        dataSourceMap.put(DataSourceConstants.DS_KEY_SLAVE, slaveDataSource());//设置动态数据源DynamicDataSource dynamicDataSource = new DynamicDataSource();dynamicDataSource.setTargetDataSources(dataSourceMap);//dynamicDataSource.setDefaultTargetDataSource(masterDataSource());//dynamicDataSource.setDefaultTargetDataSource(multiDataSourceProperties.getDatasource().get("master"));return dynamicDataSource;}
}

7、动态数据源名称上下文处理

/*** 动态数据源名称上下文处理**/
public class DynamicDataSourceContextHolder {/*** 动态数据源名称上下文*/private static final ThreadLocal<String> DATASOURCE_CONTEXT_KEY_HOLDER = new ThreadLocal<>();/*** 设置数据源* @param key*/public static void setContextKey(String key){DATASOURCE_CONTEXT_KEY_HOLDER.set(key);}/*** 获取数据源名称* @return*/public static String getContextKey(){String key = DATASOURCE_CONTEXT_KEY_HOLDER.get();return key == null?DataSourceConstants.DS_KEY_MASTER:key;}/*** 删除当前数据源名称*/public static void removeContextKey(){DATASOURCE_CONTEXT_KEY_HOLDER.remove();}
}

8、动态配置的映射,可以自定义

import java.util.Map;
public class MultiDataSourceProperties {private String primary = "master";private Map<String, MultiDataSourceVo> datasource;public  MultiDataSourceProperties() {}public String getPrimary() {return this.primary;}public void setPrimary(String primary) {this.primary = primary;}public Map<String, MultiDataSourceVo> getDatasource() {return this.datasource;}public void setDatasource(Map<String, MultiDataSourceVo> datasource) {this.datasource = datasource;}public MultiDataSourceVo getPrimaryMultiDataSourceVo() {return this.datasource != null && !this.datasource.isEmpty() ? (MultiDataSourceVo)this.datasource.get(this.primary) : null;}
}

9、动态属性的实体类

import lombok.Data;@Data
public class MultiDataSourceVo {private String driverClassName = "com.mysql.cj.jdbc.Driver";private String url;private String username;private String password;
}

10、yaml文件的配置

yixiu.cube.center.multi-datasource:enable: true #开启多数据源配置,关闭单个数据源配置primary: master #设置默认的数据源或者数据源组,默认值即为masterdatasource:master:driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始⽀持SPI可省略此配url: jdbc:mysql://aliyuncs.com:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8username: xkcpassword: 123456slave:url: jdbc:mysql://192.168.2.2:30306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8username: xkcpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave1:url: jdbc:mysql://192.168.2.2:30306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8username: xkcpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver

11、编写接口添加数据就可以在目标数据库查看了,这个简单就不说明了。其他的配置,可以查看官方说明或者博客资料。

12、参考项目:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter

相关文章:

springboot动态使用DruidDataSource切换数据源(动态配置多个数据源)

1、添加依赖&#xff0c;在pom文件中添加 <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>2.5.0</version></dependency><dependency><grou…...

P8786 [蓝桥杯 2022 省 B] 李白打酒加强版

【dfs题解】---只有50分 (头一回自己用dfs做出来了dp的hard等级的大题&#xff0c;从来没有拿50分这么高兴过哈哈哈哈哈) #include <bits/stdc.h> using namespace std; int n,m; long long ans0; const long long mol1e97; void dfs(int h,int d,int sum) {if(h<0|…...

没有网没有移动存储的情况下两台电脑如何互相传输数据

无网无移动存储情况下两台电脑数据互传探秘 一、直连网线传输数据二、局域网文件共享其他 在信息化时代的今天&#xff0c;电脑作为重要的数据处理工具&#xff0c;在日常生活和工作中扮演着不可或缺的角色。然而&#xff0c;有时我们会遇到一些特殊情况&#xff0c;如没有网络…...

如何用putty通过ssh连接ubuntu

1. 下载和安装PuTTY 访问PuTTY官网下载PuTTY的最新版本。 2. 打开PuTTY 解压下载的文件后&#xff0c;找到PuTTY文件并双击打开。 3. 配置SSH连接 在ubuntu下安装ssh服务在安装ssh时&#xff0c;我一直遇到一个问题&#xff0c;原因是我的虚拟机连不上网&#xff0c;反复实…...

java如何实现rabbitmq的消息确认机制和消息持久化机制配置和示例

在Java中&#xff0c;使用RabbitMQ的客户端库&#xff08;通常是AMQP客户端库&#xff0c;如RabbitMQ的Java客户端&#xff09;可以方便地实现消息确认机制和消息持久化机制。以下是如何实现这两个机制的示例。 1、消息确认机制 RabbitMQ支持两种类型的确认&#xff1a;生产者…...

react 组件:Suspense

允许在子组件完成加载前展示后备方案。 children&#xff1a;真正的 UI 渲染内容。如果 children 在渲染中被挂起&#xff0c;Suspense 边界将会渲染 fallback。 fallback&#xff1a;真正的 UI 未渲染完成时代替其渲染的备用 UI&#xff0c;它可以是任何有效的 React 节点。后…...

2024-4-5修改vscode的代理

今天在vs code 上面配置go环境的时候出现了以下的报错&#xff1a; 2024-04-05 16:18:00.786 [info] Installing golang.org/x/tools/goplslatest FAILED 2024-04-05 16:18:00.786 [info] { “code”: 1, “killed”: false, “signal”: null, “cmd”: “E:\Go\bin\go.exe in…...

python字符切片的规则

跟range一样有三个参数&#xff0c;分别是x:y:z&#xff0c;代表的含义分别为左边界&#xff0c;右边界&#xff08;注意该范围是左闭右开的&#xff0c;也就是说取不到右值&#xff09;和步长。 1. 切片是从左往右还是从右往左&#xff0c;看的是步长的正负&#xff0c;如果步…...

C++ 的内存安全与效率

在C编程中&#xff0c;内存安全和效率是两个至关重要的考虑因素。 内存安全涉及确保程序在分配和使用内存时不会发生错误&#xff0c;如内存泄漏、悬挂指针、越界访问、空指针解引用等&#xff1b; 效率则关注如何有效地使用内存资源&#xff0c;减少不必要的内存分配和释放操…...

Go 实战|使用 Wails 构建轻量级的桌面应用:仿微信登录界面 Demo

概述 本文探讨 Wails 框架的使用&#xff0c;从搭建环境到开发&#xff0c;再到最终的构建打包&#xff0c;本项目源码 GitHub 地址&#xff1a;https://github.com/mazeyqian/go-run-wechat-demo 前言 Wails 是一个跨平台桌面应用开发框架&#xff0c;他允许开发者利用 Go …...

c++取经之路(其五)——类和对象拷贝构造函数

概念&#xff1a;拷贝构造函数&#xff0c;只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&#xff0c;在用已存在的类类型对象创建新对象时由编译器自动调用。 特征&#xff1a; 1. 拷贝构造函数是构造函数的一个重载形式 如&#xff1a; 2. 拷贝…...

YOLOv8最新改进系列:融合最新顶会提出的HCANet网络中卷积和注意力融合模块(CAFM),有效提升小目标检测性能,大幅度拉升目标检测效果!遥遥领先!

YOLOv8最新改进系列&#xff1a;YOLOv8最新改进系列:融合最新顶会提出的HCANet网络中卷积和注意力融合模块(CAFM)&#xff0c;有效提升小目标检测性能&#xff0c;大幅度拉升目标检测效果&#xff01;遥遥领先&#xff01; B站全文戳这里&#xff01; 详细的改进教程以及源码…...

【计算机毕业设计】网上宠物商店管理系统——后附源码

&#x1f389;**欢迎来到我的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 一名来自世界500强的资深程序媛&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于…...

详解Qt添加外部库

在Qt项目中添加外部库是一项常见任务&#xff0c;无论是静态库还是动态库都需要正确的配置才能让项目顺利编译链接。以下是详细步骤和不同场景下的配置方法&#xff1a; 方法一&#xff1a;手动编辑.pro文件 添加头文件路径&#xff1a; 在Qt项目中的.pro文件中使用INCLUDEPAT…...

深入理解JVM垃圾收集器

相关系列 深入理解JVM垃圾收集算法-CSDN博客 目前市面常见的垃圾收集器有Serial、ParNew、Parallel、CMS、Serial Old、Parallel Old、G1、ZGC以及有二种不常见的Epsilon、Shenandoah的&#xff0c;从上图可以看到有连线的的垃圾收集器是可以组合使用&#xff0c;是年轻代老年代…...

macU盘在电脑上读不出来 u盘mac读不出来怎么办 macu盘不能写入 Tuxera NTFS for Mac免费下载

对于Mac用户来说&#xff0c;使用U盘是很常见的操作&#xff0c;但有时候可能会遇到Mac电脑无法读取U盘的情况&#xff0c;这时候就需要使用一些特定的工具软件来帮助我们解决问题。本文就来告诉大家macU盘在电脑上读不出来是怎么回事&#xff0c;u盘mac读不出来怎么办。 一、m…...

448.找到所有数组中消失的数字(原地修改)

给你一个含 n 个整数的数组 nums &#xff0c;其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字&#xff0c;并以数组的形式返回结果。 示例 1&#xff1a; 输入&#xff1a;nums [4,3,2,7,8,2,3,1] 输出&#xff1a;[5,6] 原地修改 …...

Redis学习从入门到掌握(基础篇)

文章目录 一、初识Redis1.认识 Redis2.Redis常见命令&#xff08;1&#xff09;Redis 数据结构介绍&#xff08;2&#xff09;Redis 通用命令&#xff08;3&#xff09;String 类型&#xff08;4&#xff09;String 类型的常见命令&#xff08;5&#xff09;Hash 类型&#xff…...

redis主从复制、哨兵

目录 1. 主从复制 特点&#xff1a; 工作原理&#xff1a; 配置&#xff1a; 2. 哨兵 特点&#xff1a; 工作原理&#xff1a; 配置&#xff1a; ​编辑 1. 主从复制 特点&#xff1a; 主从复制是 Redis 最基本的高可用性方案。主节点&#xff08;Master&#xff09…...

uniapp登录拦截白名单使用

1、创建uni.promisify.adaptor.js文件 // 根目录新建 uni.promisify.adaptor.js // 路由白名单 const list [/, /pages/stroke/stroke]; //创建路由拦截&#xff0c;这里只判断一般跳转 uni.addInterceptor(switchTab, {invoke(res) {console.log(res);//存在token就跳转if (…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…...