当前位置: 首页 > 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 (…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...