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

基于Mybatis继承AbstractRoutingDataSource使用自定义注解实现动态数据源

一:实现

方式一:继承AbstractRoutingDataSource使用自定义注解实现

环境:springboot3 + MyBatis3 + mysql-connector8

DataSourceKeyEnum枚举类

有几个数据源就配置几个枚举类,和数据源数量一一对应

class DataSourceKeyEnum{DEFAULT,SLAVE
}

DataSourceAnnotation注解

用来标记在Service方法或Mapper上,用于辅助AOP切换数据源操作。

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceAnnotation {DataSourceKeyEnum value() default DataSourceKeyEnum.DEFAULT;}

AbstractRoutingDataSource 的作用

AbstractRoutingDataSource的核心功能是根据用户定义的规则,动态决定在特定的操作中使用哪个数据源。这通常是通过重写determineCurrentLookupKey方法来实现的,该方法用于确定当前线程应该使用哪个数据源的键。

public class DynamicDataSource extends AbstractRoutingDataSource {/***  MyBatis内部根据实现AbstractRoutingDataSource类的determineCurrentLookupKey实现数据源的动态路由*/@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceType();}/***  静态内部类,用于从线程中获取当前要使用的数据源*/public static class DynamicDataSourceContextHolder {private static final ThreadLocal<DataSourceKeyEnum> contextHolder = new ThreadLocal<>();public static DataSourceKeyEnum getDataSourceType() {return contextHolder.get();}public static void setDataSourceType(DataSourceKeyEnum dataSourceKey) {if (dataSourceKey == null) {dataSourceKey = DataSourceKeyEnum.DEFAULT;}contextHolder.set(dataSourceKey);}public static void clearDataSourceType() {contextHolder.remove();}}
}

DataSourceConfig数据源配置类

环境配置

spring.datasource.default.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.default.jdbcUrl=jdbc:mysql://xxxxxx/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true
spring.datasource.default.username=root
spring.datasource.default.password=rootspring.datasource.slave.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.slave.jdbcUrl=jdbc:mysql://xxxxxx/mybatis?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.slave.username=root
spring.datasource.slave.password=rootmapper.identity=MYSQL
mapper.wrap-keyword= `{0}`
mybatis.type-aliases-package=com.example.simplejobdemo.entity
mybatis.mapper-locations=classpath:mapper/*.xml
##mybatis.mapper-locations=classpath:mapper/base/**/*.xml,mapper/common/**/*.xml,mapper/core/**/*.xml,workflow/*.xml,com/sitech/pgcenter/mapper/core/*.xml,base/**/*.xmlmybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis.configuration.jdbc-type-for-null=null

创建数据源,对于动态数据源一定要加上 @Primary注解。 

@Configuration
public class DataSourceConfig {@Bean@ConfigurationProperties(prefix = "spring.datasource.default")public DataSource dataSourceDefault() {return DataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "spring.datasource.slave")public DataSource dataSourceSlave() {return DataSourceBuilder.create().build();}/*** 动态数据源* @param dataSourceDefault 默认数据源* @param dataSourceSlave 从数据源* @return*/@Bean@Primarypublic DataSource dataSource(DataSource dataSourceDefault,DataSource dataSourceSlave) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceKeyEnum.DEFAULT, dataSourceDefault);targetDataSources.put(DataSourceKeyEnum.SLAVE, dataSourceSlave);DynamicDataSource dataSource = new DynamicDataSource();dataSource.setTargetDataSources(targetDataSources);dataSource.setDefaultTargetDataSource(dataSourceSlave);return dataSource;}}

DataSourceAspect切面类

@Aspect
@Component
public class DataSourceAspect {//切点@Pointcut("@annotation(com.example.simplejobdemo.config.datasourceconf.DataSourceAnnotation)")public void pointCut() {}@Around(value = "pointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();DataSourceAnnotation dataSourceAnnotation = method.getAnnotation(DataSourceAnnotation.class);DataSourceKeyEnum dataSourceKeyEnum = dataSourceAnnotation.value();if(dataSourceKeyEnum == null) {dataSourceKeyEnum = DataSourceKeyEnum.DEFAULT;}DynamicDataSource.DynamicDataSourceContextHolder.setDataSourceType(dataSourceKeyEnum);try {return joinPoint.proceed();}finally {DynamicDataSource.DynamicDataSourceContextHolder.clearDataSourceType();}}}

二:原理

AbstractRoutingDataSource类如何影响正在执行mybatis执行sql语句的数据源选择?

每个sql执行时,调用DataSource接口的getConnection()方法获取数据库的连接,实际执行是会使用AbstractRoutingDataSource类的getConnection()选取指定数据的连接(AbstractRoutingDataSource实现了DataSource接口),这就是项目中每个执行sql可以使用AbstractRoutingDataSource类中指定数据源的原因。

 这里getConnection方法调用时的核心方法determineTargetDataSourc()

核心方法是determineCurrentLookupKey(),通过这个方法调用获取业务指定的的map中key值,获取resolvedDataSources中指定key的数据源 。

determineCurrentLookupKey()的实现在自定义的子类方法中,自定义的子类方法中,将key值业务定义使用的是本地线程栈技术

运用本地线程栈,可以在线程中手动或者自动(拦截器)将key值设定到本地线程栈对象contextHolder中,当sql执行时会,对调用AbstractRoutingDataSource的getConnection(),再调用determineTargetDataSource(),子类中determineCurrentLookupKey()最终决定了从本地线程栈对象contextHolder获取当前数据源

如何初始化所有数据源?

继续看AbstractRoutingDataSource类的核心方法determineTargetDataSourc()

AbstractRoutingDataSource类本身有个四个核心属性:

//用来通过配置文件指定所有的key值和value数据源
private Map<Object, Object> targetDataSources;
//用来通过配置文件指定默认数据源
private Object defaultTargetDataSource;
//在afterPropertiesSet方法将defaultTargetDataSource中转为resolvedDefaultDataSource,这个是后面存储默认数据源
private DataSource resolvedDefaultDataSource;
//在afterPropertiesSet方法将targetDataSources中转为resolvedDataSources,这个是后面存储使用的名称-数据源映射
private Map<Object, DataSource> resolvedDataSources;

 其中resolvedDataSources又是如何初始化的?

AbstractRoutingDataSource类实现了InitializingBean接口,所以此方法在这个bean初始化时执行afterPropertiesSet方法。afterPropertiesSet方法将配置文件中注入的targetDataSources和defaultTargetDataSource 转换为了后续可以使用的resolvedDefaultDataSource(默认数据源)和resolvedDataSources(存储使用的名称-数据源映射)

而targetDataSources的初始化,就是我们通过配置实现的

相关文章:

基于Mybatis继承AbstractRoutingDataSource使用自定义注解实现动态数据源

一&#xff1a;实现 方式一&#xff1a;继承AbstractRoutingDataSource使用自定义注解实现 环境&#xff1a;springboot3 MyBatis3 mysql-connector8 DataSourceKeyEnum枚举类 有几个数据源就配置几个枚举类&#xff0c;和数据源数量一一对应 class DataSourceKeyEnum{D…...

ZooKeeper 数据模型

ZooKeeper 数据模型 ZooKeeper 拥有层次化的命名空间&#xff0c;类似分布式文件系统&#xff0c;但每个节点不仅能有子节点&#xff0c;还可关联数据。节点路径为规范的绝对路径&#xff0c;用斜杠分隔&#xff0c;无相对引用。路径命名有如下约束&#xff1a; 路径名不能包…...

【VUE】Vue2中Vue.extend方法

在 Vue.js 2.x 版本中&#xff0c;Vue.extend() 方法被用于创建一个新的 Vue 子类&#xff0c;可以在该子类上扩展一些属性、指令和组件选项等&#xff0c;然后进行实例化。 比如&#xff0c;可以在创建一些类似 loading 式的函数式插件时&#xff0c;使用&#xff1a; 在 Vue…...

MaskGAE论文阅读

What’s Behind the Mask: Understanding Masked Graph Modeling for Graph Autoencoders 碎碎念&#xff1a;一篇论文看四天&#xff0c;效率也没谁了(捂脸) 看一点忘一点&#xff0c;虽然在本子上有记录&#xff0c;但还是忘&#xff0c;下次看一点在博客上记一点启发 本来很…...

Mybatis-plus 更新 Null 的策略踩坑记

一个bug 在一个管理页面&#xff0c;有一个非必填字段被设置成空了并提交更新&#xff0c;再次打开的时候&#xff0c;发现字段还在&#xff0c;并没有被更新成功。 使用的数据库映射框架是 Mybatis-plus &#xff0c;对于Mybatis 在更新字段的时候会对空进行校验&#xff0c;…...

Oracle迁移DM数据库

Oracle迁移DM数据库 本文记录使用达梦官方数据迁移工具DTS&#xff0c;将Oracle数据库的数据迁移至达梦数据库。 1 数据准备 2 DTS工具操作步骤 2.1 创建工程 打开DTS迁移工具&#xff0c;点击新建工程&#xff0c;填写好工程信息&#xff0c;如图&#xff1a; 2.2 新建迁…...

HTML特殊符号的使用示例

目录 一、基本特殊符号的使用 1、空格符号&#xff1a; 2、小于号 和 大于号&#xff1a; 3、引号&#xff1a; 二、版权、注册商标符号的使用 1、版权符号&#xff1a;© 2、注册商标符号&#xff1a; 三、数学符号的使用 四、箭头符号的使用 五、货币符号的使用…...

数据结构基础之《(15)—排序算法小结》

一、排序算法的稳定性 1、稳定性是指同样大小的样本再排序之后不会改变相对次序 2、对基础类型来说&#xff0c;稳定性毫无意义 比如&#xff1a;3和3没有区别。《潜伏》里说同样两个一百元大钞&#xff0c;你能告诉我哪一个是高尚的那一个是龌龊的么 3、对非基础类型来说&a…...

Linux系统下速通stm32的clion开发环境配置

陆陆续续搞这个已经很久了。 因为自己新电脑是linux系统无法使用keil&#xff0c;一开始想使用vscode里的eide但感觉不太好用&#xff1b;后面想直接使用cudeide但又不想妥协&#xff0c;想趁着这个机会把linux上的其他单片机开发配置也搞明白&#xff1b;而且非常想搞懂cmake…...

【2024年 CSDN博客之星】我的2024年创作之旅:从C语言到人工智能,个人成长与突破的全景回顾

我的2024年创作之旅&#xff1a;从C语言到人工智能&#xff0c;个人成长与突破的全景回顾 引言 回望2024年&#xff0c;我不仅收获了技术上的成长&#xff0c;更收获了来自CSDN平台上无数粉丝、朋友以及网友们的支持与鼓励。在这条创作之路上&#xff0c;CSDN不仅是我展示技术成…...

Python 轻松扫描,快速检测:高效IP网段扫描工具全解析

Python 轻松扫描&#xff0c;快速检测&#xff1a;高效IP网段扫描工具全解析 相关资源文件已经打包成EXE文件&#xff0c;可双击直接运行程序&#xff0c;且文章末尾已附上相关源码&#xff0c;以供大家学习交流&#xff0c;博主主页还有更多Python相关程序案例&#xff0c;秉着…...

go入门Windows环境搭建

简介 Go 即 Golang&#xff0c;是 Google 公司 2009 年 11 月正式对外公开的一门编程语言。 根据 Go 语言开发者自述&#xff0c;近 10 多年&#xff0c;从单机时代的 C 语言到现在互联网时代的 Java&#xff0c;都没有令人满意的开发语言&#xff0c;而 C往往给人的感觉是&a…...

安装Ubuntu22.04

1.引用教程 如何安装Ubuntu Server 22.04 LTS_ubuntu22.04 server-CSDN博客 2.空间分配 要使用 docker 比较多所以分别的 docker 空间大...

对比OpenAI的AI智能体Operator和智谱的GLM-PC,它们有哪些不同?

OpenAI 的 AI 智能体 Operator 和智谱的 GLM-PC 有以下不同&#xff1a; 功能侧重 Operator&#xff1a;主要侧重于网页操作&#xff0c;能在网页上模拟人类进行点击、输入等操作&#xff0c;完成如预订旅行住宿、餐厅预约、在线购物、在 Arxiv 上进行论文分类搜索等任务123。…...

Git Bash 配置 zsh

博客食用更佳 博客链接 安装 zsh 安装 Zsh 安装 Oh-my-zsh github仓库 sh -c "$(curl -fsSL https://install.ohmyz.sh/)"让 zsh 成为 git bash 默认终端 vi ~/.bashrc写入&#xff1a; if [ -t 1 ]; thenexec zsh fisource ~/.bashrc再重启即可。 更换主题 …...

美格智能AIMO智能体+DeepSeek-R1模型,AI应用的iPhone时刻来了

导语&#xff1a; 当AI大模型从云端下沉至终端设备&#xff0c;一场关于效率、隐私与智能化的革命悄然展开。作为全球领先的无线通信模组及解决方案提供商&#xff0c;美格智能凭借其高算力AI模组矩阵与端侧大模型部署经验&#xff0c;结合最新发布的AIMO智能体产品&#xff0…...

Python标准库 - os (1) 环境变量、进程的用户和组

文章目录 1 访问和修改环境变量1.1 访问环境变量1.2 修改环境变量 2 进程的用户和组2.1 进程的ID2.2 进程的用户2.3 进程组 os模块提供了各种操作系统接口。包括环境变量、进程管理、进程调度、文件操作等方面。 这里整理了环境变量、进程的用户和用户组相关的控制方法。 参考…...

QT 通过ODBC连接数据库的好方法:

效果图&#xff1a; PWD使用自己的&#xff0c;我的这是自己的&#xff0c;所以你用不了。 以下是格式。 // 1. 设置数据库连接 QSqlDatabase db QSqlDatabase::addDatabase("QODBC");// 建立和QMYSQL数据库的连接 // 设置数据库连接名称&#xff08;DSN&am…...

机器学习 - 初学者需要弄懂的一些线性代数的概念

一、单位矩阵 在数学中&#xff0c;单位矩阵是一个方阵&#xff0c;其主对角线上的元素全为1&#xff0c;其余元素全为0。单位矩阵在矩阵乘法中起到类似于数字1在数值乘法中的作用&#xff0c;即任何矩阵与单位矩阵相乘&#xff0c;结果仍为原矩阵本身。 单位矩阵的定义&…...

WordPress event-monster插件存在信息泄露漏洞(CVE-2024-11396)

免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...

Python竞赛环境搭建全攻略

Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型&#xff08;算法、数据分析、机器学习等&#xff09;不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!

目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...

2.3 物理层设备

在这个视频中&#xff0c;我们要学习工作在物理层的两种网络设备&#xff0c;分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间&#xff0c;需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质&#xff0c;假设A节点要给…...