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

MySQL技巧之跨服务器数据查询:进阶篇-从A服务器的MySQ数据库复制到B服务器的SQL Server数据库的表中
MySQL技巧之跨服务器数据查询:进阶篇-从A服务器的MySQ数据库复制到B服务器的SQL Server数据库的表中 基础篇已经描述:借用微软的SQL Server ODBC 即可实现MySQL跨服务器间的数据查询。 而且还介绍了如何获得一个在MS SQL Server 可以连接指定实例的MyS…...
大语言模型LLM的微调中 QA 转换的小工具 xlsx2json.py
在训练语言模型中,需要将文件整理成规范的文档,因为文档本身会有很多不规范的地方,为了训练的正确,将文档进行规范处理。代码的功能是读取一个 Excel 文件,将其数据转换为 JSON 格式,并将 JSON 数据写入到一…...

CFD 在生物反应器放大过程中的作用
工艺工程师最常想到的一个问题是“如何将台式反应器扩大到工业规模的反应器?”。这个问题的答案并不简单,也不容易得到。例如,人们误以为工业规模的反应器的性能与台式反应器相同。因此,扩大规模的过程并不是一件容易的事。必须对…...
Axios与FastAPI结合:构建并请求用户增删改查接口
在现代Web开发中,FastAPI以其高性能和简洁的代码结构成为了构建RESTful API的热门选择。而Axios则因其基于Promise的HTTP客户端特性,成为了前端与后端交互的理想工具。本文将介绍FastAPI和Axios的结合使用,通过一个用户增删改查(C…...

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

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

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 概念:存储唯一值的集合,元素只能是值,没有键与之对应。Set中的每个值都是唯一的。 特性: 值的集合,值可以是任何类型。 值的唯一性,每个值只能出现一次。 保持了插入顺序。 不支持通过索引来…...
万能门店小程序管理系统存在前台任意文件上传漏洞
免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在…...
详解Rust泛型用法
文章目录 基础语法泛型与结构体泛型约束泛型与生命周期泛型与枚举泛型和Vec静态泛型(const 泛型)类型别名默认类型参数Sized Trait与泛型常量函数与泛型泛型的性能 Rust是一种系统编程语言,它拥有强大的泛型支持,泛型是Rust中用于实现代码复用和类型安全…...

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

Mybatis:Mybatis快速入门
Mybatis的官方文档是真的非常好!非常好! 点一下我呗:Mybatis官方文档 MyBatis 是一款优秀的持久层框架,它支持自定义 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的学习花的时间比较长,如何进行正确的学习?今天进行总结与整理。 首先,明确JavaScript是什么?它的结构框架是什么,有哪些操作与组成部分。 其次,通过案例实践,清楚达到什么效果…...
Python 3 教程第22篇(数据结构)
Python3 数据结构 本章节我们主要结合前面所学的知识点来介绍Python数据结构。 列表 Python中列表是可变的,这是它区别于字符串和元组的最重要的特点,一句话概括即:列表可以修改,而字符串和元组不能。 以下是 Python 中列表的方…...

AI时代的软件工程:迎接LLM-DevOps的新纪元
在科技日新月异的今天,GPT的问世无疑为各行各业带来了一场深刻的变革,而软件工程领域更是首当其冲,正式迈入了软件工程3.0的新纪元。2024年,作为软件工程3.0的元年,伴随着软件工程3.0宣言的震撼发布,一个全…...
linux安全管理-系统环境安全
1 历史命令设置 1、检查内容 检查操作系统的历史命令设置。 2、配置要求 建议操作系统的历史命令设置。 3、配置方法 编辑/etc/profile 文件,配置保留历史命令的条数 HISTSIZE 和保留历史命令的记录文件大小 HISTFILESIZE,这两个都设置为 5。 配置方法如…...

MindAgent部署(进行中.....)
第一步:pip install -r requirements.txt 问题:如下:就是我的服务器,无法访问github Preparing metadata (setup.py) ... errorerror: subprocess-exited-with-error python setup.py egg_info did not run successfully.│ exi…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构
React 实战项目:微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇!在前 29 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...