springboot动态切换数据源

1、创建一个springboot项目,导入依赖(3.3.0)
 <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.6.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!--Druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
2、实现ThreadLocal

创建一个类用于实现ThreadLocal,主要是通过get,set,remove方法来获取、设置、删除当前线程对应的数据源。

public class DataSourceContextHolder {//此类提供线程局部变量。这些变量不同于它们的正常对应关系是每个线程访问一个线程(通过get、set方法),有自己的独立初始化变量的副本。private static final ThreadLocal<String> DATASOURCE_HOLDER = new ThreadLocal<>();/*** 设置数据源* @param dataSourceName 数据源名称*/public static void setDataSource(String dataSourceName){DATASOURCE_HOLDER.set(dataSourceName);}/*** 获取当前线程的数据源* @return 数据源名称*/public static String getDataSource(){return DATASOURCE_HOLDER.get();}/*** 删除当前数据源*/public static void removeDataSource(){DATASOURCE_HOLDER.remove();}
}
3、实现AbstractRoutingDataSource

定义一个动态数据源类实现AbstractRoutingDataSource,通过determineCurrentLookupKey方法与上述实现的ThreadLocal类中的get方法进行关联,实现动态切换数据源。

/*** @description: 实现动态数据源,根据AbstractRoutingDataSource路由到不同数据源中* @date: 2023/7/27 11:18**/
public class DynamicDataSource extends AbstractRoutingDataSource {public DynamicDataSource(DataSource defaultDataSource,Map<Object, Object> targetDataSources){super.setDefaultTargetDataSource(defaultDataSource);super.setTargetDataSources(targetDataSources);}@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSource();}
}

上述代码中,还实现了一个动态数据源类的构造方法,主要是为了设置默认数据源,以及以Map保存的各种目标数据源。其中Map的key是设置的数据源名称,value则是对应的数据源(DataSource)。

4、配置数据库

application.yml中配置数据库信息:

#设置数据源
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedruid:master:url: jdbc:mysql://xxxxxx:3306/test1?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave:url: jdbc:mysql://xxxxx:3306/test2?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverinitial-size: 15min-idle: 15max-active: 200max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: ""test-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: falseconnection-properties: false
mybatis:mapper-locations: mappers/**/*.xmlconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl

配置类

/*** @description: 设置数据源* @date: 2023/7/27 11:34**/
@Configuration
public class DateSourceConfig {@Bean@ConfigurationProperties("spring.datasource.druid.master")public DataSource masterDataSource(){return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties("spring.datasource.druid.slave")public DataSource slaveDataSource(){return DruidDataSourceBuilder.create().build();}@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource createDynamicDataSource(){Map<Object,Object> dataSourceMap = new HashMap<>();DataSource defaultDataSource = masterDataSource();dataSourceMap.put("master",defaultDataSource);dataSourceMap.put("slave",slaveDataSource());return new DynamicDataSource(defaultDataSource,dataSourceMap);}
}

通过配置类,将配置文件中的配置的数据库信息转换成datasource,并添加到DynamicDataSource中,同时通过@Bean将DynamicDataSource注入Spring中进行管理,后期在进行动态数据源添加时,会用到。

5、测试
@GetMapping("/getData.do/{datasourceName}")
public String getMasterData(@PathVariable("datasourceName") String datasourceName){DataSourceContextHolder.setDataSource(datasourceName);TestUser testUser = testUserMapper.selectOne(null);DataSourceContextHolder.removeDataSource();return testUser.getUserName();
}

通过执行结果,我们看到传递不同的数据源名称,查询对应的数据库是不一样的,返回结果也不一样。
在上述代码中,我们看到DataSourceContextHolder.setDataSource(datasourceName); 来设置了当前线程需要查询的数据库,通过DataSourceContextHolder.removeDataSource(); 来移除当前线程已设置的数据源。

注:启动程序时,小伙伴不要忘记将SpringBoot自动添加数据源进行排除哦,否则会报循环依赖问题。

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
6、优化调整(注解切换数据源)

在上述中,虽然已经实现了动态切换数据源,但是我们会发现如果涉及到多个业务进行切换数据源的话,我们就需要在每一个实现类中添加这一段代码。
说到这有小伙伴应该就会想到使用注解来进行优化,接下来我们来实现一下。

6.1 定义注解

我们就用mybatis动态数据源切换的注解:DS,代码如下:

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DS {String value() default "master";
}
6、2 实现aop
@Aspect
@Component
@Slf4j
public class DSAspect {    @Pointcut("@annotation(com.jiashn.dynamic_datasource.dynamic.aop.DS)")public void dynamicDataSource(){}@Around("dynamicDataSource()")public Object datasourceAround(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature)point.getSignature();Method method = signature.getMethod();DS ds = method.getAnnotation(DS.class);if (Objects.nonNull(ds)){DataSourceContextHolder.setDataSource(ds.value());}try {return point.proceed();} finally {DataSourceContextHolder.removeDataSource();}}
}

代码使用了@Around,通过ProceedingJoinPoint获取注解信息,拿到注解传递值,然后设置当前线程的数据源

6、4 测试
@GetMapping("/getMasterData.do")
public String getMasterData(){TestUser testUser = testUserMapper.selectOne(null);return testUser.getUserName();
}
@GetMapping("/getSlaveData.do")
@DS("slave")
public String getSlaveData(){TestUser testUser = testUserMapper.selectOne(null);return testUser.getUserName();
}

注意也可以同时操作多个数据源,只需要在相应方法上添加 @DS注解就可以,(service)
由于@DS中设置的默认值是:master,因此在调用主数据源时,可以不用进行添加。

通过执行结果,我们通过@DS也进行了数据源的切换,实现了Mybatis动态切换数据源中的通过注解切换数据源的方式。

第二种方式使用第三方框架,dynamic-datasource多数源组件
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.6.1</version></dependency>
配置数据源
spring:application:name: DynamicSourceDatadatasource:dynamic:#??????????????,?????masterprimary: master#???????,??false. true?????????????,false???????strict: falsedatasource:master:url: jdbc:mysql://192.168.64.140:3306/datasource1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave_1:url: jdbc:mysql://192.168.64.136:3306/datasource2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivercache:type: redisdata:redis:host: localhostport: 6379#password: 123456database: 0 #????0????jedis:#Redis?????pool:max-active: 8 #?????max-wait: 1ms #???????????max-idle: 4 #???????????min-idle: 0 #???????????
mybatis:mapper-locations: mappers/**/*.xmlconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl
测试使用
@RestController
@RequestMapping("/friend")
public class FriendController {@Autowiredprivate FriendService friendService;//  主库查询@PostMapping("/find")public List<Friend> getListDatas() {return friendService.findListDatas();}/*** 注意:@DS("dsName") dsName可以为组名也可以为具体某个库的名称* 没有@DS  默认数据源* 3.使用 @DS 切换数据源。* @DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解* @return*///   操作从库@DS("slave_1")@PostMapping("/find1")public List<Friend> getListDatas1() {return friendService.findListDatas();}// 主从库同时操作@PostMapping("/find2")@DSTransactionalpublic List<Friend> getListDatas2() {
//        操作主库数据List<Friend> masters = friendService.findMastDatas();
//        操作从库数据List<Friend> salves = friendService.findSlaveDatas();masters.addAll(salves);System.out.println(masters);return masters;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/21126.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

渗透测试靶机----FirstLeads_1.3

渗透测试靶机----FirstLeads_1.3 启动靶机&#xff0c;扫描ip&#xff0c;平平无奇 可以看出&#xff0c;这里是存在139这个主机的&#xff0c;这就是扫描出来的靶机ip继续探测端口及其他信息 发现这里只开启了80 端口 尝试一些基本目录&#xff0c;发现存在robot.txt文件目录…

集成算法:Bagging模型、AdaBoost模型和Stacking模型

概述 目的&#xff1a;让机器学习效果更好&#xff0c;单个不行&#xff0c;集成多个 集成算法 Bagging&#xff1a;训练多个分类器取平均 f ( x ) 1 / M ∑ m 1 M f m ( x ) f(x)1/M\sum^M_{m1}{f_m(x)} f(x)1/M∑m1M​fm​(x) Boosting&#xff1a;从弱学习器开始加强&am…

插入排序以及希尔排序; 先学会插入,希尔会更简单喔

1.前言 首先肯定是要学会插入排序再学习希尔排序会更简单&#xff0c;因为代码部分有很多相似之处&#xff1b;如果你觉得你很强&#xff0c;可以直接看希尔排序的讲解。哈哈哈&#xff01;&#xff0c;每天进步一点点&#xff0c;和昨天的自己比 2.插入排序 让我们先来看看…

鸿蒙Ability Kit(程序框架服务)【UIAbility组件与UI的数据同步】

UIAbility组件与UI的数据同步 基于当前的应用模型&#xff0c;可以通过以下几种方式来实现UIAbility组件与UI之间的数据同步。 [使用EventHub进行数据通信]&#xff1a;在基类Context中提供了EventHub对象&#xff0c;可以通过发布订阅方式来实现事件的传递。在事件传递前&am…

Rustdesk 自建服务器教程

一、环境 阿里云轻量服务器、debian11 系统 二、服务端搭建 2.1、开放防火墙指定端口 TCP(21115, 21116, 21117, 21118, 21119)UDP(21116) 2.2、安装 rustdesk 服务器文件 在 github 下载页https://github.com/rustdesk/rustdesk-server/releases/&#xff0c;下载 rustde…

【自撰写,国际象棋入门】第1课、棋盘和棋子

第1课 棋盘和棋子 一、国际象棋的棋盘 国际象棋的棋盘为一8乘8的黑、白格相间的棋盘&#xff0c;8条竖线的编号分别为A-H&#xff0c;8条横线的编号分别为1-8&#xff0c;在记谱时用竖线编号横线编号的方式表示棋盘上的格子&#xff0c;例如a1格、h8格等.棋盘上有几条重要的大…

c++程序员为什么要做自己的底层库

五一期间&#xff0c;在家里翻到之前上学时候用的电脑和工作日志&#xff0c;粗略浏览一番&#xff0c;感慨10年岁月蹉跎&#xff0c;仍然没有找到自己技术方向的“道”。遂有感而发&#xff0c;写下此文。 算起来&#xff0c;接触软件开发也有10年时间了&#xff0c;最开始是…

Java——异常

1.什么是异常 将程序执行过程中发生的不正常行为称为异常。 常见的异常有&#xff1a;算数异常&#xff0c;空指针异常&#xff0c;数组越界异常 每一种异常都有对应的类对齐描述 为了对每一种异常进行管理&#xff0c;Java内部实现了一个对异常的体系结构 1. Throwable&#x…

CS2游戏30万挂箱账号被封,饰品市场要变天

Steam游戏平台上CS2的玩家在线人数常年位于第一位&#xff0c;即便偶尔会被爆款游戏挤下来&#xff0c;但一切都是暂时的。饰品交易作为CS2的重要组成部分&#xff0c;早已成为了维系游戏热度的不二法门。可相对应的&#xff0c;各种挂箱子的工作室及个人也孕育而生。 但近来V社…

mysql多启动

binary安装&#xff1a; 1、redhat rpm 2、mysql rpm 3、mysql glibc source安装&#xff1a; 1、5.1mysql(./configure && make && make install) 2、5.5mysql(cmake && make && make install) 单启动&#xff1a; 1、安装 tar xf xxx.tar…

【Docker学习】docker pull详细说明

docker pull是我们经常用到的一个命令。我们使用一些官方镜像&#xff0c;如MySql、Nginx等都需要用docker pull下载。不过不用的话&#xff0c;也可以。比如使用docker run&#xff0c;要是找不到镜像&#xff0c;会自动下载。 命令&#xff1a; docker image pull 描述&am…

Uniapp写一个简单的商品瀑布流界面+商品详情

最终效果&#xff1a; 整体内容比较简单&#xff0c;参考了一篇瀑布流文章和一篇商品详情文章随便修改整了下&#xff0c;主要是给想做这方便面的新人一个简单逻辑的展示&#xff08;其实我也是第一次写这个emmm&#xff09; 一.组件下载&#xff1a; uni-icon uni-goods-nav…

什么是ACP?

前言 ACP指的是应用程序控制平面&#xff0c;是微服务架构中的一个关键组成部分。它负责管理微服务架构中的各个微服务&#xff0c;包括服务发现和注册、负载均衡、服务路由、熔断和降级、配置管理等方面的功能。 A&#xff1a;可用性 所有请求都有响应。C&#xff1a;强一致…

[DDR5 Jedec 3-4] 模式寄存器 Mode Register MRR/MRW

依公知及经验整理,原创保护,禁止转载。 专栏 《深入理解DDR》 1. 概念 模式寄存器用于定义各种操作模式。在初始化过程中,可以通过重新执行MRS命令来更改模式寄存器的内容。即使用户只想修改模式寄存器变量的一个子集,在发出MRS命令时也必须编程所有变量。 只有当所有ban…

C语言案例-输入任意三个数,按从大到小的顺序输出.

目录 问题待续、更新中 问题 输入任意三个数,按从大到小的顺序输出. 最大值 3数&#xff0c;重新排序输出 输出数据if来&#xff0c;ab ac bc比&#xff0c;比中里面交换值&#xff0c;输出abc时为降序 代码如下: #include <stdio.h> void main() {int a,b,c,t;printf(&…

现实残酷!存款百万只是少数人的游戏,普通家庭能存多少?

近期&#xff0c;网络上掀起了一股关于普通家庭终身存款上限的热烈讨论。一位网友通过简单的算术方式提出了一个假设&#xff1a;如果一对夫妻每年收入15万&#xff0c;并成功将6万存入银行&#xff0c;那么从25岁步入社会至60岁退休&#xff0c;他们理论上能积累到210万的存款…

从0开发一个Chrome插件:Manifest 文件详解

前言 这是《从0开发一个Chrome插件》系列的第六篇文章,本系列教你如何从0去开发一个Chrome插件,每篇文章都会好好打磨,写清楚我在开发过程遇到的问题,还有开发经验和技巧。 专栏: 从0开发一个Chrome插件:什么是Chrome插件?从0开发一个Chrome插件:开发Chrome插件的必要…

C++知识点总结(36):二分进阶练习

二分答案练习 一、愤怒的羊驼题目描述输入描述输出描述样例1提示参考答案 二、偷吃西瓜题目描述输入描述输出描述样例1提示参考答案 三、丢沙包题目描述输入描述输出描述样例1提示参考答案 四、木材加工题目描述输入描述输出描述样例1提示参考答案 五、路标设置题目描述输入描述…

Go语言之GORM框架(四)——预加载,关联标签与多态关联,自定义数据类型与事务(完结篇)

前言 本来是想着写多表关系的&#xff0c;不过写了一半发现重复的部分太多了&#xff0c;想了想与其做一些重复性工作&#xff0c;不如把一些当时觉得抽象的东西记录一下&#xff0c;就当用一篇杂记完成专栏的最后一篇文章吧。 预加载 简单示例 预加载主要用于在多表关系中…

谷歌浏览器的平替,内置开挂神器,我已爱不释手!

油猴浏览器正式版是一款基于谷歌Chromium源码开发的浏览器&#xff0c;它集成了集成了强大的油猴扩展&#xff08;Tampermonkey&#xff09;&#xff0c;使得用户可以轻松安装各种脚本&#xff0c;从而增强网页浏览体验。提供了一个更加个性化和高效的浏览体验。 油猴扩展&…