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;}
}

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

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

相关文章

Flink 任务启动脚本-V2(包括ck启动)

#!/bin/bash#crontab时设置&#xff0c;如果依赖其他环境变量配置&#xff0c;可以在脚本执行一下环境变量脚本 source /etc/profile# 进入脚本目录 curdirdirname "$0" curdircd "$curdir"; pwd echo "进入启动脚本目录 $curdir"# 定义应用程序…

C#设计模式——抽象工厂模式(重点)

文章目录 项目地址一、抽象工厂模式1.1 特性1.2 使用反射获取特性标记的类1.3 完整代码 项目地址 教程作者&#xff1a;教程地址&#xff1a; 代码仓库地址&#xff1a; 所用到的框架和插件&#xff1a; dbt airflow一、抽象工厂模式 工厂方法模式依然存在一个问题就是&…

LAMP环境的部署

一、软件安装介绍 在Linux系统中安装软件有rpm安装、yum安装、源码安装等方法&#xff0c;在这里主要给大家介绍 yum 安装&#xff0c;这是一种最简单方便的一种安装方法。 YUM&#xff08;Yellow dog Upadate Modifie&#xff09;是改进版的 RPM 管理器&#xff0c;很好地解…

『VUE』elementUI dialog的子组件created生命周期不刷新(详细图文注释)

目录 1. 测试代码分析令人迷惑的效果 分析原因解决方法 如何在dialog中反复触发created呢?总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 主要是在做表单的时候想要有一个编辑表单在dialog弹窗中出现,同时dialog调用的封装的…

项目实战:基于深度学习的人脸表情识别系统设计与实现

大家好&#xff0c;人脸表情识别是计算机视觉领域中的一个重要研究方向&#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. 鼠标悬停 三、主题 …

Chrome和edge浏览器如何为任何网站强制暗模式

前言 因为我的编辑器是黑色&#xff0c;可能是看的时间长了比较喜欢这种颜色了&#xff0c;感觉白色有些刺眼。尤其是看文章时&#xff0c;两边的空白纯白色&#xff0c;所以强迫症搜素设置了谷歌浏览器和edge如何设置成黑色。 Chrome和edge浏览器如何为任何网站强制暗模式 前…

使用 Elastic 收集 Windows 遥测数据:ETW Filebeat 输入简介

作者&#xff1a;来自 Elastic Chema Martinez 在安全领域&#xff0c;能够使用 Windows 主机的系统遥测数据为监控、故障排除和保护 IT 环境开辟了新的可能性。意识到这一点&#xff0c;Elastic 推出了专注于 Windows 事件跟踪 (ETW) 的新功能 - 这是一种强大的 Windows 原生机…

k8s网络服务

k8s 中向外界提供服务的几种方法port-forward、NodePort&#xff0c;以及 更加常用的提供服务的资源ingress。 1 kubectl port-forward service/redis 6379:6379 现在k8s中有一个pod运行在6379&#xff0c;本机访问映射到6379上&#xff0c;它可以针对部署&#xff0c;服务&…

手动设置 IP 地址和使用 DHCP 在以下方面存在区别

手动设置 IP 地址和使用 DHCP 在以下方面存在区别&#xff1a; 配置过程 手动设置 IP 地址&#xff1a;需要用户手动输入 IP 地址、子网掩码、默认网关、DNS 服务器地址等网络配置参数。在 Windows 系统中&#xff0c;通常要打开 “控制面板”&#xff0c;进入 “网络和共享中…

Python网络爬虫基础

Python网络爬虫是一种自动化工具&#xff0c;用于从互联网上抓取信息。它通过模拟人类浏览网页的行为&#xff0c;自动地访问网站并提取所需的数据。网络爬虫在数据挖掘、搜索引擎优化、市场研究等多个领域都有广泛的应用。以下是Python网络爬虫的一些基本概念&#xff1a; 1.…

实现跨语言通信:Rust 和 Thrift 的最佳实践

前言 在分布式系统中&#xff0c;服务之间高效且安全的通信至关重要。Apache Thrift 是一个被广泛应用的跨语言 RPC&#xff08;远程过程调用&#xff09;框架&#xff0c;它支持多种编程语言&#xff0c;包括 Rust。Rust 以其卓越的性能和内存安全保障&#xff0c;成为越来越…

微信小程序按字母顺序渲染城市 功能实现详细讲解

在微信小程序功能搭建中&#xff0c;按字母渲染城市会用到多个ES6的方法&#xff0c;如reduce&#xff0c;map&#xff0c;Object.entries()&#xff0c;Object.keys() &#xff0c;需要组合熟练掌握&#xff0c;才能优雅的处理数据完成渲染。 目录 一、数据分析 二、数据处理 …

框架学习07 - SpringMVC 其他功能实现

一. 拦截器实现HandlerInterceptor 接⼝ SpringMVC 中的 Interceptor 拦截器也是相当重要和相当有⽤的&#xff0c;它的主要作⽤是拦截⽤户的请求并进⾏相应的处理。⽐如通过它来进⾏权限验证&#xff0c;或者是来判断⽤户是否登陆等操作。对于 SpringMVC 拦截器的定义⽅式有两…

前端JavaScript(一)---基本介绍

Javascript是一种由Netscape(网景)的LiveScript发展而来的原型化继承的面向对象的动态类型的区分大小写的客户端脚本语言&#xff0c;主要目的是为了解决服务器端语言&#xff0c;比如Perl&#xff0c;遗留的速度问题&#xff0c;为客户提供更流畅的浏览效果。当时服务端需要对…

git的使用(简洁版)

什么是 Git&#xff1f; Git 是一个分布式版本控制系统 (DVCS)&#xff0c;用于跟踪文件的更改并协调多人之间的工作。它由 Linus Torvalds 在 2005 年创建&#xff0c;最初是为了管理 Linux 内核的开发。Git 的主要目标是提供高效、易用的版本控制工具&#xff0c;使得开发者…

设计模式之破环单例模式和阻止破坏

目录 1. 序列化和反序列化2. 反射 这里单例模式就不多说了 23种设计模式之单例模式 1. 序列化和反序列化 这里用饿汉式来做例子 LazySingleton import java.io.Serializable;public class LazySingleton implements Serializable {private static LazySingleton lazySinglet…

kotlin 的循环

循环的步长可以通过step关键字进行设置 在Kotlin中&#xff0c;循环的默认步长为1。如果需要改变步长&#xff0c;可以在循环表达式中使用step关键字。例如&#xff0c;要从0循环到100&#xff0c;步长为2&#xff0c;可以这样写&#xff1a; for (i in 0..100 step 2) {prin…

vscode可以编译通过c++项目,但头文件有红色波浪线的问题

1、打开 VSCode 的设置&#xff0c;可以通过快捷键 Ctrl Shift P 打开命令面板&#xff0c;然后搜索并选择 “C/C: Edit Configurations (JSON)” 命令&#xff0c;这将在 .vscode 文件夹中创建或修改 c_cpp_properties.json 文件 {"configurations": [{"name…

Java与Kotlin在鸿蒙中的地位

在当今移动操作系统领域&#xff0c;华为推出的鸿蒙系统&#xff08;HarmonyOS&#xff09;正逐渐崭露头角&#xff0c;成为与Android、iOS并驾齐驱的操作系统之一。对于开发者而言&#xff0c;了解如何为鸿蒙系统开发高质量的应用程序变得至关重要。在这篇文章中&#xff0c;我…