基于AbstractRoutingDataSource的mybatis动态多数据源切换

1.pom

mybatis-starter版本只能选2开头的版本,选3开头的就报错

<!--druid连接池-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.3</version>
</dependency><!--mybatis-->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.1</version>
</dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version><scope>runtime</scope>
</dependency><!--        切面依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.yml配置

server:port: 9005servlet:context-path: /
# CREATE USER 'test1'@'localhost' IDENTIFIED BY 'test1'; 创建test1 用户#FLUSH PRIVILEGES;  刷新#grant all privileges on study.* to 'test1'@'localhost' with grant option;  把库study 授权给test1
spring:datasource:druid:master:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/study?characterEncoding=utf-8username: test1password: test1#type: com.alibaba.druid.pool.DruidDataSourceslave:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/studyslave?characterEncoding=utf-8username: test1password: test1#type: com.alibaba.druid.pool.DruidDataSourceinitial-size: 5 #初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时max-active: 10 #最大连接池数量min-idle: 5 #最小连接池数量stat-view-servlet:enabled: true  # 是否启用StatvViewServlet(监控页面),默认是falsetype: com.alibaba.druid.pool.DruidDataSourcemybatis:mapper-locations: mapper/*Mapper.xmlconfiguration:map-underscore-to-camel-case: true # 驼峰命名log-impl: org.apache.ibatis.logging.stdout.StdOutImpltype-aliases-package: study.entity

3. 初始化dataSource

package study.config;import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;/*** 项目初始化的时候将各个数据源注册到DynamicDataSource 中,其中firstDataSource作为默认数据源*/
@Configuration
public class DynamicDataSourceConfig {private static String MASTER = "master";private static String SLAVE = "slave";/*** 生成masterDataSource* @return*/@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中使用@Primary注解来标记主要实例* @param masterDataSource* @param slaveDataSource* @return*/@Bean@Primarypublic DynamicRoutingDataSource dynamicDataSource(DataSource masterDataSource, DataSource slaveDataSource) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(MASTER, masterDataSource);targetDataSources.put(SLAVE, slaveDataSource);return new DynamicRoutingDataSource(masterDataSource, targetDataSources);}}

4.继承AbstractRoutingDataSource

package study.config;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;
import java.util.Map;public class DynamicRoutingDataSource extends AbstractRoutingDataSource {private static final ThreadLocal<String> DataSourceContextHolder = new ThreadLocal<>();/***** DynamicDataSourceConfig调用了这构造函数* @param defaultTargetDataSource* @param targetDataSources*/public DynamicRoutingDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {// 默认数据源super.setDefaultTargetDataSource(defaultTargetDataSource);// 所有的数据源super.setTargetDataSources(targetDataSources);// 初始化时,给resolvedDataSources 赋值super.afterPropertiesSet();}public static void setDataSource(String dataSource) {DataSourceContextHolder.set(dataSource);}public static String getDataSource() {return DataSourceContextHolder.get();}public static void clearDataSource() {DataSourceContextHolder.remove();}/*** 该类中还有一个determineTargetDataSource方法,* 是根据lookupkey从Map中获取对应的数据源,如果没有获取到,则使用默认的数据源* @return*/@Overrideprotected Object determineCurrentLookupKey() {return getDataSource();}
}

 

5.定义切换数据源的注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DS {String value() default "";
}/**
* 定义切面,获取注解对应的dataSource
*/
package study.config.annotation;import com.alibaba.druid.util.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import study.config.DynamicRoutingDataSource;import java.lang.reflect.Method;@Aspect
@Component
public class DataSourceAspect {@Pointcut("@annotation(study.config.annotation.DS)")public void logPointCut() {}@Before("logPointCut()")public void doBefore(){System.out.println("方法执行前。");}@Around("logPointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature)point.getSignature();Method method = signature.getMethod();DS ds = method.getAnnotation(DS.class);if(ds != null && !StringUtils.isEmpty(ds.value())){DynamicRoutingDataSource.setDataSource(ds.value());}try {return point.proceed();} finally {DynamicRoutingDataSource.clearDataSource();}}}

6.mapper定义

@Mapper
public interface UserMapper  {@DS("master")@Select("select id as id, user_name as userName, password as password,create_time as createTime from t_user where id = #{id}")public UserInfo selectByIdFromMaster(@Param("id") Integer id);@Select("select id as id, user_name as userName, password as password,create_time as createTime from t_user where id = #{id}")public UserInfo selectByIdDefault(@Param("id") Integer id);@DS("slave")@Select("select id as id, user_name as userName, password as password,create_time as createTime from t_user where id = #{id}")public UserInfo selectByIdFromSlave(@Param("id") Integer id);}@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {private Integer id;private String userName;private String password;private Date createTime;private Date updateTime;public UserInfo(String userName, String password, Date createTime, Date updateTime) {this.userName = userName;this.password = password;this.createTime = createTime;this.updateTime = updateTime;}
}

6.service类


@Slf4j
@Service
public class UserServiceImpl {@Resourceprivate UserMapper userMapper;public List<UserInfo> selectById(int id){UserInfo userInfo1 = selectByIdDefault(2);UserInfo userInfo2 = selectByIdFromMaster(1);UserInfo userInfo3 = selectByIdFromSlave(5);log.info("userInfo1={}, userInfo2={}, userInfo3={}", userInfo1, userInfo2, userInfo3);return Arrays.asList(userInfo1, userInfo2);}/*** 从master库查询* @param id* @return*/public UserInfo selectByIdFromMaster(int id) {UserInfo userInfo = userMapper.selectByIdFromMaster(id);return userInfo;}/*** 从默认的库:master* @param id* @return*/public UserInfo selectByIdDefault(int id) {UserInfo userInfo = userMapper.selectByIdDefault(id);return userInfo;}/*** 从slave库查询* @param id* @return*/public UserInfo selectByIdFromSlave(int id) {return userMapper.selectByIdFromSlave(id);}}

7.运行结果

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

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

相关文章

0基础学汽车 丝滑炫酷摄影教学,手机剪映特效剪辑(66节也就是)

详情介绍 0基础学汽车丝滑炫酷摄影教学,手机剪映特效剪辑 课程内容:0基础学汽车 丝滑炫酷摄影教学,手机剪映特效剪辑(66节也就是) - 百创网-源码交易平台_网站源码_商城源码_小程序源码 01 AE课前基础知识(必看).mp4 02 怎样制作德关一样丝滑的作品第二种方法(苹果mac专…

【busybox记录】【shell指令】paste

目录 内容来源&#xff1a; 【GUN】【paste】指令介绍 【busybox】【paste】指令介绍 【linux】【paste】指令介绍 使用示例&#xff1a; 合并文件的行 - 默认输出&#xff08;默认是行合并&#xff09; 合并文件的行 - 一个文件占一行 合并文件的行 - 使用指定的间隔符…

Python Web框架Django项目开发实战:多用户内容发布系统

注意:本文的下载教程,与以下文章的思路有相同点,也有不同点,最终目标只是让读者从多维度去熟练掌握本知识点。 下载教程:Python项目开发Django实战-多用户内容发布系统-编程案例解析实例详解课程教程.pdf 一、引言 在Web应用开发中,内容发布系统是一个常见的需求。这类系…

5000A信号发生器使用方法

背景 gnss工作需要使用的5000A&#xff0c;所以做成文档&#xff0c;用于其他员工学习。 下载星历数据 https://cddis.nasa.gov/archive/gnss/data/daily/2024/brdc/ 修改daily中的年份&#xff0c;就可以获取相关截至时间的星历数据 brcd数据格式 第一行记录了卫星的PRN号&a…

yolo-world:”目标检测届大模型“

AI应用开发相关目录 本专栏包括AI应用开发相关内容分享&#xff0c;包括不限于AI算法部署实施细节、AI应用后端分析服务相关概念及开发技巧、AI应用后端应用服务相关概念及开发技巧、AI应用前端实现路径及开发技巧 适用于具备一定算法及Python使用基础的人群 AI应用开发流程概…

科技早报 | 微软将推出自研AI大模型;苹果折叠屏iPhone新专利获批 | 最新快讯

微软将推出自研AI大模型 5月6日消息&#xff0c;据The Information报道&#xff0c;微软正在公司内部训练一个新的人工智能模型&#xff0c;规模足以与谷歌、Anthropic&#xff0c;乃至OpenAI 自身的先进大模型相抗衡。 报道称&#xff0c;这个新模型内部代号为“MAI-1”&…

【AcWing】800. 数组元素的目标和

原题链接&#xff1a;活动 - AcWing 目录 1. 题目描述 输入格式 输出格式 数据范围 输入样例&#xff1a; 输出样例&#xff1a; 2. 思路分析 3. 代码实现 1. 题目描述 给定两个升序排序的有序数组 A 和 B&#xff0c;以及一个目标值 x。 数组下标从 0 开始。 请你…

2024牛客五一集训派对day1 C. Gifted Composer 【二分Hash、循环节】

C. Gifted Composer 传送门 题意 一个字符串初始为空&#xff0c;每次操作可以在其开头或末尾添加一个字符&#xff0c;对每次操作后的字符串&#xff0c;询问有多少个不同的循环节长度&#xff1f; 思路 首先&#xff0c;对于循环节的判断&#xff0c;我们可以用哈希&…

深度学习实战76-基于目标检测YOLOv5模型的迁移学习使用方法,YOLOv5的原理与结构

大家好,我是微学AI,今天给大家介绍一下深度学习实战76-基于目标检测YOLOv5模型的迁移学习使用方法,YOLOv5的原理与结构。YOLOv5(You Only Look Once version 5)是一种先进的目标检测算法,基于深度学习的单阶段目标检测模型。它的主要原理是通过一次前向传播就同时预测图像…

Redis是什么? 日常运维 Redis 需要注意什么 ? 怎么降低Redis 内存使用 节省内存?

你的项目或许已经使用 Redis 很长时间了&#xff0c;但在使用过程中&#xff0c;你可能还会或多或少地遇到以下问题&#xff1a; 我的 Redis 内存为什么增长这么快&#xff1f;为什么我的 Redis 操作延迟变大了&#xff1f;如何降低 Redis 故障发生的频率&#xff1f;日常运维…

c++ priority_queue用法

1 priority_queue的定义 基本定义方法&#xff1a; 基本定义默认是使用大顶堆的&#xff0c;即队首总是最大的元素 priority_queue<int> q;//储存int型数据 priority_queue<double> q;//储存double型数据 priority_queue<string> q;//储存string型数据 …

英语学习笔记1——Excuse me!

Excuse me! 对不起&#xff01; 词汇 Vocabulary excuse v. 原谅 [iks’kju:z] 用法&#xff1a;1. Excuse me! 对不起&#xff01; 用于以下场景&#xff1a; 向陌生人问路 牢记引起别人注意中途离开某个场所。发出怪声&#xff08;如打喷嚏之后&#xff09; 和 I’m sorr…

详解AI作画算法原理

AI作画算法的原理通常涉及深度学习和计算机视觉技术。下面是一个简要的解释&#xff1a; 数据准备&#xff1a;首先&#xff0c;需要准备大量的绘画数据&#xff0c;包括各种风格和类型的绘画作品。这些数据通常由艺术家的作品组成&#xff0c;可以包括油画、水彩画、素描等。 …

【C++】 constexpr 关键字的使用和示例

constexpr是C中一个重要的关键字&#xff0c;它用于声明可以在编译时期计算的变量和函数。这意味着使用constexpr声明的实体可以在编译时求值&#xff0c;从而可以作为模板参数或数组大小等需要编译时确定的上下文中使用 1. 定义变量 constexpr变量必须在定义时初始化&#x…

【代码随想录算法训练营第37期 第一天 | LeetCode704. 二分查找、27. 移除元素】

代码随想录算法训练营第37期 第一天 | LeetCode704. 二分查找、27. 移除元素 一、704. 二分查找 解题代码C&#xff1a; class Solution { public:int search(vector<int>& nums, int target) {int len nums.size();int l 0, r len - 1;while(l < r){int mid…

大数据与会计专业主要学什么课程

大数据与会计专业是一个结合了传统会计知识与现代大数据技术的交叉学科&#xff0c;旨在培养既懂会计又熟悉大数据分析的复合型人才。该专业的学生将会学习以下主要课程内容&#xff1a; 会计基础课程&#xff1a;包括基础会计、财务会计、成本会计、管理会计等&#xff0c;这些…

有了这么多套件,为什么还需要APaaS

文/明道云创始人任向晖 在明道云的业务活动中&#xff0c;比较常见的一个问题是和套件应用的关系。一个有具体应用需求的客户为什么不从市场上购买现成的套件应用&#xff0c;而要选择APaaS来构建呢&#xff1f;反过来说似乎也成立&#xff0c;既然一个平台什么应用都能搭建&a…

使用双指针解决问题题集(二)

1. 有效三角形的个数 给定一个包含非负整数的数组 nums &#xff0c;返回其中可以组成三角形三条边的三元组个数。 示例 1: 输入: nums [2,2,3,4] 输出: 3 解释:有效的组合是: 2,3,4 (使用第一个 2) 2,3,4 (使用第二个 2) 2,2,3 示例 2: 输入: nums [4,2,3,4] 输出: 4 题解&a…

软件系统测试方案书(测试计划-Word原件)

2 引言 2.1 编写目的 2.3 测试人员 2.4 项目背景 2.5 测试目标 2.6 简写和缩略词 2.7 参考资料 2.8 测试提交文档 2.9 测试进度 3 测试环境 3.1 软硬件环境 4 测试工具 5 测试策略 5.1 测试阶段划分及内容 5.1.1 集成测试 5.1.2 系统测试 5.1.2.1 功能测试 5.…

深入解析智能指针:从实践到原理

&#x1f466;个人主页&#xff1a;晚风相伴 &#x1f440;如果觉得内容对你有所帮助的话&#xff0c;还请一键三连&#xff08;点赞、关注、收藏&#xff09;哦 如果内容有错或者不足的话&#xff0c;还望你能指出。 目录 智能指针的引入 内存泄漏 RAII 智能指针的使用及原…