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

Spring Boot 动态数据源切换

背景

随着互联网应用的快速发展,多数据源的需求日益增多。Spring Boot 以其简洁的配置和强大的功能,成为实现动态数据源切换的理想选择。本文将通过具体的配置和代码示例,详细介绍如何在 Spring Boot 应用中实现动态数据源切换,帮助开发者高效应对不同业务场景下的数据管理需求。无论是读写分离还是数据隔离,都能轻松搞定。

AOP动态代理

AOP注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {String name();
}

AOP切面类

@Aspect
@Component
public class DataSourceAspect {@Pointcut("@annotation(com.example.aliyunai.db.TargetDataSource)")public void dataSourcePointcut() {}@Before("dataSourcePointcut()")public void changeDataSource(JoinPoint point) {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();TargetDataSource targetDataSource = method.getAnnotation(TargetDataSource.class);if (targetDataSource != null) {String dataSourceName = targetDataSource.name();DataSourceContextHolder.setDataSourceKey(dataSourceName);}}@After("dataSourcePointcut()")public void clearDataSource(JoinPoint point) {DataSourceContextHolder.clearDataSourceKey();}
}

数据源配置

@Configuration
public class DataSourceConfig {@Bean(name = "master")public DataSource primaryDataSource() {return DataSourceBuilder.create().type(HikariDataSource.class).driverClassName("").url("").username("").password("").build();}@Bean(name = "slave")public DataSource secondaryDataSource() {return DataSourceBuilder.create().type(HikariDataSource.class).driverClassName("").url("").username("").password("").build();}@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dynamicDataSource(@Qualifier("master") DataSource master,@Qualifier("slave") DataSource slave) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put("master", master);targetDataSources.put("slave", slave);DynamicDataSource dynamicDataSource = new DynamicDataSource();dynamicDataSource.setTargetDataSources(targetDataSources);dynamicDataSource.setDefaultTargetDataSource(master);return dynamicDataSource;}}

线程上下文


public class DataSourceContextHolder {private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();public static void setDataSourceKey(String key) {contextHolder.set(key);}public static String getDataSourceKey() {return contextHolder.get();}public static void clearDataSourceKey() {contextHolder.remove();}
}

动态数据源设置

public class DynamicDataSource extends AbstractRoutingDataSource {private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);@Overrideprotected Object determineCurrentLookupKey() {String dataSourceKey = DataSourceContextHolder.getDataSourceKey();logger.info("Determining current data source: {}", dataSourceKey);return dataSourceKey;}
}

service类

@Service
public class UserService {@Resourceprivate UserMapper userMapper;@TargetDataSource(name = "master")public User queryFromPrimary() {User user = userMapper.selectById(1);return user;}@TargetDataSource(name = "slave")public User queryFromSecondary() {User user = userMapper.selectById(1L);return user;}
}

Mybatis 拦截器


@Component
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = Executor.class, method = "update", args = {MappedStatement.class,Object.class})})
public class DynamicDataSourceInterceptor implements Interceptor {private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceInterceptor.class);// 假设这里有一个获取当前数据源标识的方法,你需要根据实际项目中的实现来替换private String getCurrentDataSourceKey() {// 示例:从某个上下文持有者中获取数据源标识,这里只是示意,实际要替换return DataSourceContextHolder.getDataSourceKey();}@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];Object parameter = invocation.getArgs()[1];BoundSql boundSql = mappedStatement.getBoundSql(parameter);String sql = boundSql.getSql();//对单个表进行处理logger.info("sql:"+sql);String mappedStatementId = mappedStatement.getId();//对所有查询进行处理if (mappedStatementId.contains("query")) {String table = getTable(sql);if (table.equals("user")){DataSourceContextHolder.setDataSourceKey(getCurrentDataSourceKey());}//增删改}else if (mappedStatementId.contains("update")) {DataSourceContextHolder.setDataSourceKey(getCurrentDataSourceKey());}return invocation.proceed();}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}private static String getTable(String sql) {// 定义正则表达式模式,用于匹配 "from" 和 "where" 之间的表名Pattern pattern = Pattern.compile("FROM\\s+(\\w+)\\s+WHERE");Matcher matcher = pattern.matcher(sql);if (matcher.find()) {return matcher.group(1);}return null;}
}

相关文章:

Spring Boot 动态数据源切换

背景 随着互联网应用的快速发展&#xff0c;多数据源的需求日益增多。Spring Boot 以其简洁的配置和强大的功能&#xff0c;成为实现动态数据源切换的理想选择。本文将通过具体的配置和代码示例&#xff0c;详细介绍如何在 Spring Boot 应用中实现动态数据源切换&#xff0c;帮…...

MySQL技巧之跨服务器数据查询:进阶篇-从A服务器的MySQ数据库复制到B服务器的SQL Server数据库的表中

MySQL技巧之跨服务器数据查询&#xff1a;进阶篇-从A服务器的MySQ数据库复制到B服务器的SQL Server数据库的表中 基础篇已经描述&#xff1a;借用微软的SQL Server ODBC 即可实现MySQL跨服务器间的数据查询。 而且还介绍了如何获得一个在MS SQL Server 可以连接指定实例的MyS…...

大语言模型LLM的微调中 QA 转换的小工具 xlsx2json.py

在训练语言模型中&#xff0c;需要将文件整理成规范的文档&#xff0c;因为文档本身会有很多不规范的地方&#xff0c;为了训练的正确&#xff0c;将文档进行规范处理。代码的功能是读取一个 Excel 文件&#xff0c;将其数据转换为 JSON 格式&#xff0c;并将 JSON 数据写入到一…...

CFD 在生物反应器放大过程中的作用

工艺工程师最常想到的一个问题是“如何将台式反应器扩大到工业规模的反应器&#xff1f;”。这个问题的答案并不简单&#xff0c;也不容易得到。例如&#xff0c;人们误以为工业规模的反应器的性能与台式反应器相同。因此&#xff0c;扩大规模的过程并不是一件容易的事。必须对…...

Axios与FastAPI结合:构建并请求用户增删改查接口

在现代Web开发中&#xff0c;FastAPI以其高性能和简洁的代码结构成为了构建RESTful API的热门选择。而Axios则因其基于Promise的HTTP客户端特性&#xff0c;成为了前端与后端交互的理想工具。本文将介绍FastAPI和Axios的结合使用&#xff0c;通过一个用户增删改查&#xff08;C…...

美畅物联丨如何通过ffmpeg排查视频问题

在我们日常使用畅联AIoT开放云平台的过程中&#xff0c;摄像机视频无法播放是较为常见的故障。尤其是当碰到摄像机视频不能正常播放的状况时&#xff0c;哪怕重启摄像机&#xff0c;也仍然无法使其恢复正常的工作状态&#xff0c;这着实让人感到头疼。这个时候&#xff0c;可以…...

基于OpenCV视觉库让机械手根据视觉判断物体有无和分类抓取的例程

项目实例&#xff0c;在一个无人封闭的隔绝场景中&#xff0c;根据视觉判断物件的有无&#xff0c;通过机械手 进行物件分类提取&#xff0c;并且返回状态结果&#xff1b; 实际的场景是有一个类似采血的固件支架盘&#xff0c;上面很多采血管&#xff0c;采血管帽颜色可能不同…...

QChart数据可视化

目录 一、QChart基本介绍 1.1 QChart基本概念与用途 1.2 主要类的介绍 1.2.1 QChartView类 1.2.2 QChart类 1.2.3QAbstractSeries类 1.2.4 QAbstractAxis类 1.2.5 QLegendMarker 二、与图表交互 1. 动态绘制数据 2. 深入数据 3. 缩放和滚动 4. 鼠标悬停 三、主题 …...

转换的艺术:如何在JavaScript中序列化Set为Array、Object及逆向操作

先认识一下Set 概念&#xff1a;存储唯一值的集合&#xff0c;元素只能是值&#xff0c;没有键与之对应。Set中的每个值都是唯一的。 特性&#xff1a; 值的集合&#xff0c;值可以是任何类型。 值的唯一性&#xff0c;每个值只能出现一次。 保持了插入顺序。 不支持通过索引来…...

万能门店小程序管理系统存在前台任意文件上传漏洞

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

详解Rust泛型用法

文章目录 基础语法泛型与结构体泛型约束泛型与生命周期泛型与枚举泛型和Vec静态泛型(const 泛型)类型别名默认类型参数Sized Trait与泛型常量函数与泛型泛型的性能 Rust是一种系统编程语言&#xff0c;它拥有强大的泛型支持&#xff0c;泛型是Rust中用于实现代码复用和类型安全…...

移远通信携手紫光展锐,以“5G+算力”共绘万物智联新蓝图

11月26日&#xff0c;2024紫光展锐全球合作伙伴大会在上海举办。作为紫光展锐重要的合作伙伴&#xff0c;移远通信应邀参会。 在下午的物联网生态论坛上&#xff0c;移远通信产品总监胡勇华作题为“5G与算力双擎驱动 引领智联新未来”的演讲&#xff0c;深度剖析了产业发展的趋…...

Mybatis:Mybatis快速入门

Mybatis的官方文档是真的非常好&#xff01;非常好&#xff01; 点一下我呗&#xff1a;Mybatis官方文档 MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可…...

微信小程序用户登录页面制作教程

微信小程序用户登录页面制作教程 前言 在微信小程序的开发过程中,用户登录是一个至关重要的功能。通过用户登录,我们可以为用户提供个性化的体验,保护用户数据,并实现更复杂的业务逻辑。本文将为您详细讲解如何制作一个用户登录页面,包括设计思路、代码示例以及实现细节…...

python+django自动化平台(一键执行sql) 前端vue-element展示

一、开发环境搭建和配置 pip install mysql-connector-pythonpip install PyMySQL二、django模块目录 dbOperations ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-313.pyc │ ├── admin.cpython-313.pyc │ ├── apps.cpython-313.pyc │ …...

JavaScript学习总结

前言 JavaScript的学习花的时间比较长&#xff0c;如何进行正确的学习&#xff1f;今天进行总结与整理。 首先&#xff0c;明确JavaScript是什么&#xff1f;它的结构框架是什么&#xff0c;有哪些操作与组成部分。 其次&#xff0c;通过案例实践&#xff0c;清楚达到什么效果…...

Python 3 教程第22篇(数据结构)

Python3 数据结构 本章节我们主要结合前面所学的知识点来介绍Python数据结构。 列表 Python中列表是可变的&#xff0c;这是它区别于字符串和元组的最重要的特点&#xff0c;一句话概括即&#xff1a;列表可以修改&#xff0c;而字符串和元组不能。 以下是 Python 中列表的方…...

AI时代的软件工程:迎接LLM-DevOps的新纪元

在科技日新月异的今天&#xff0c;GPT的问世无疑为各行各业带来了一场深刻的变革&#xff0c;而软件工程领域更是首当其冲&#xff0c;正式迈入了软件工程3.0的新纪元。2024年&#xff0c;作为软件工程3.0的元年&#xff0c;伴随着软件工程3.0宣言的震撼发布&#xff0c;一个全…...

linux安全管理-系统环境安全

1 历史命令设置 1、检查内容 检查操作系统的历史命令设置。 2、配置要求 建议操作系统的历史命令设置。 3、配置方法 编辑/etc/profile 文件&#xff0c;配置保留历史命令的条数 HISTSIZE 和保留历史命令的记录文件大小 HISTFILESIZE&#xff0c;这两个都设置为 5。 配置方法如…...

MindAgent部署(进行中.....)

第一步&#xff1a;pip install -r requirements.txt 问题&#xff1a;如下&#xff1a;就是我的服务器&#xff0c;无法访问github Preparing metadata (setup.py) ... errorerror: subprocess-exited-with-error python setup.py egg_info did not run successfully.│ exi…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

uniapp 小程序 学习(一)

利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 &#xff1a;开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置&#xff0c;将微信开发者工具放入到Hbuilder中&#xff0c; 打开后出现 如下 bug 解…...