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

【Spring】DynamicDataSourceHolder 动态数据源切换

【Spring】DynamicDataSourceHolder 动态数据源切换

    • 常见场景
    • 常见工具
    • 一、AbstractRoutingDataSource
      • 1.1、 定义 DynamicDataSourceHolder
      • 1.2、 配置动态数据源
      • 1.3、 在Spring配置中定义数据源
      • 1.4、在业务代码中切换数据源
    • 二、Dynamic Datasource for Spring Boot
      • 2.1. 添加依赖
      • 2.2. 配置数据源
    • 2.3. 使用注解切换数据源
      • 2.4. 启动类配置
    • 三、阿里巴巴的Druid
      • 3.1. 引入依赖
      • 3.2. 配置多数据源
      • 3.3. 实现动态数据源路由
      • 3.4. 创建数据源上下文持有者
      • 3.5. 配置数据源
      • 3.6. 使用动态数据源

一个开发系统需要用多个数据库怎么办呢?可不可以连接多个数据库?
当然可以!随意切换。
DynamicDataSourceHolder 通常是用于动态数据源切换的一个工具类,特别是在多数据源场景下使用。它通过 ThreadLocal 来保存当前线程的数据源标识,以便在同一线程中能够动态切换不同的数据源。

常见场景

在实际项目中,可能需要根据不同的业务需求在多个数据源之间进行切换。比如,读写分离(主从库)、多租户架构等。通过DynamicDataSourceHolder可以方便地实现这一需求。

常见工具

有许多现有的动态数据源管理工具和框架,可以帮助简化多数据源配置和管理。以下是一些常用的工具和框架:

  1. Spring Boot DataSource Routing with AbstractRoutingDataSource
    Spring本身提供了 AbstractRoutingDataSource 类,可以用于实现动态数据源路由。

  2. Spring Cloud DataSource Routing
    在Spring Cloud环境中,可以使用Spring Cloud提供的配置和工具实现动态数据源管理。

  3. MyBatis Dynamic Datasource
    MyBatis提供了一个动态数据源插件mybatis-spring,可以用于在MyBatis中实现动态数据源切换。

  4. Dynamic Datasource for Spring Boot
    动态数据源管理库 dynamic-datasource-spring-boot-starter,是一个简单易用的Spring Boot动态数据源启动器,支持多种数据源配置和切换。

  5. Druid Dynamic Datasource
    阿里巴巴的Druid数据源也支持动态数据源切换,可以通过配置Druid的相关属性实现。

下面详细介绍一下 Spring本身提供的 AbstractRoutingDataSource 、SpringBoot 动态数据启动器、以及现在常用的阿里巴巴的Druid数据源。

一、AbstractRoutingDataSource

1.1、 定义 DynamicDataSourceHolder

public class DynamicDataSourceHolder {private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();/*** 设置数据源* @param dataSourceKey 数据源标识*/public static void setDataSource(String dataSourceKey) {contextHolder.set(dataSourceKey);}/*** 获取数据源* @return 数据源标识*/public static String getDataSource() {return contextHolder.get();}/*** 清除数据源*/public static void clearDataSource() {contextHolder.remove();}
}

1.2、 配置动态数据源

需要配置一个 AbstractRoutingDataSource 来实现动态数据源的路由。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicRoutingDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceHolder.getDataSource();}
}

1.3、 在Spring配置中定义数据源

在Spring配置中定义数据源,并将动态数据源配置为主数据源。

import org.springframework.beans.factory.annotation.Qualifier;
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 org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;@Configuration
public class DataSourceConfig {@Bean(name = "dataSource1")@ConfigurationProperties(prefix = "spring.datasource.ds1")public DataSource dataSource1() {return DataSourceBuilder.create().build();}@Bean(name = "dataSource2")@ConfigurationProperties(prefix = "spring.datasource.ds2")public DataSource dataSource2() {return DataSourceBuilder.create().build();}@Primary@Bean(name = "dataSource")public DataSource dataSource(@Qualifier("dataSource1") DataSource dataSource1,@Qualifier("dataSource2") DataSource dataSource2) {DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put("ds1", dataSource1);targetDataSources.put("ds2", dataSource2);dynamicRoutingDataSource.setTargetDataSources(targetDataSources);dynamicRoutingDataSource.setDefaultTargetDataSource(dataSource1);return dynamicRoutingDataSource;}
}

1.4、在业务代码中切换数据源

在需要切换数据源的业务逻辑中使用 DynamicDataSourceHolder 来设置当前数据源。

@Service
public class SomeService {@Transactionalpublic void someMethod() {// 切换到数据源 ds1DynamicDataSourceHolder.setDataSource("ds1");// 执行与数据源 ds1 相关的操作// 切换到数据源 ds2DynamicDataSourceHolder.setDataSource("ds2");// 执行与数据源 ds2 相关的操作// 恢复到默认数据源DynamicDataSourceHolder.clearDataSource();}
}

DynamicDataSourceHolder 通过 ThreadLocal 实现了线程级别的数据源切换,结合 AbstractRoutingDataSource 实现动态数据源路由,可以方便地在多数据源场景下进行数据源的动态切换。需要注意的是,在使用动态数据源时,要确保在合适的时机清除线程本地变量,以防止数据源混乱。

二、Dynamic Datasource for Spring Boot

2.1. 添加依赖

在pom.xml中添加dynamic-datasource-spring-boot-starter依赖:

<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.4.0</version>
</dependency>

2.2. 配置数据源

在application.yml或application.properties中配置多个数据源:

spring:datasource:dynamic:primary: masterdatasource:master:url: jdbc:mysql://localhost:3306/master_dbusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driverslave:url: jdbc:mysql://localhost:3306/slave_dbusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver

2.3. 使用注解切换数据源

使用@DS 注解在需要的方法或类上指定数据源:

import com.baomidou.dynamic.datasource.annotation.DS;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class UserService {@DS("slave")@Transactionalpublic void getSlaveData() {// 逻辑使用从库数据源}@DS("master")@Transactionalpublic void getMasterData() {// 逻辑使用主库数据源}
}

2.4. 启动类配置

在Spring Boot启动类中启用动态数据源支持:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

三、阿里巴巴的Druid

阿里巴巴的Druid 是一个高效、稳定且功能强大的数据库连接池,支持监控、日志输出和多种数据库管理功能。Druid也支持动态数据源切换,通过配置Druid的相关属性和使用Spring的AbstractRoutingDataSource来实现。

3.1. 引入依赖

在你的Spring Boot项目中引入Druid依赖:

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version>
</dependency>

3.2. 配置多数据源

application.yml 中配置多个数据源。Druid支持的数据源配置属性非常丰富,可以配置连接池大小、超时设置、日志等。

spring:datasource:dynamic:primary: masterdatasource:master:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/master_dbusername: rootpassword: rootdruid:initial-size: 5min-idle: 5max-active: 20max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: SELECT 1 FROM DUALtest-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: truemax-pool-prepared-statement-per-connection-size: 20filters: stat,wall,log4jslave:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/slave_dbusername: rootpassword: rootdruid:initial-size: 5min-idle: 5max-active: 20max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: SELECT 1 FROM DUALtest-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: truemax-pool-prepared-statement-per-connection-size: 20filters: stat,wall,log4j

3.3. 实现动态数据源路由

通过继承Spring的 AbstractRoutingDataSource 实现动态数据源路由逻辑。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicRoutingDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceKey();}
}

3.4. 创建数据源上下文持有者

使用 ThreadLocal 来保存当前线程的数据源标识。

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

3.5. 配置数据源

在Spring配置类中配置Druid数据源和动态数据源路由。

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.beans.factory.annotation.Qualifier;
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 org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;@Configuration
public class DataSourceConfig {@Bean@ConfigurationProperties("spring.datasource.dynamic.datasource.master")public DataSource masterDataSource() {return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties("spring.datasource.dynamic.datasource.slave")public DataSource slaveDataSource() {return DruidDataSourceBuilder.create().build();}@Primary@Beanpublic DataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource,@Qualifier("slaveDataSource") DataSource slaveDataSource) {DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put("master", masterDataSource);targetDataSources.put("slave", slaveDataSource);dynamicRoutingDataSource.setTargetDataSources(targetDataSources);dynamicRoutingDataSource.setDefaultTargetDataSource(masterDataSource);return dynamicRoutingDataSource;}
}

3.6. 使用动态数据源

在需要切换数据源的业务逻辑中使用 DynamicDataSourceContextHolder 来设置当前数据源。

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class UserService {@Transactionalpublic void useMaster() {DynamicDataSourceContextHolder.setDataSourceKey("master");// 使用主库执行操作DynamicDataSourceContextHolder.clearDataSourceKey();}@Transactionalpublic void useSlave() {DynamicDataSourceContextHolder.setDataSourceKey("slave");// 使用从库执行操作DynamicDataSourceContextHolder.clearDataSourceKey();}
}

总结
Druid Dynamic Datasource通过结合Spring的AbstractRoutingDataSource和ThreadLocal,实现了高效的动态数据源切换。配置和使用都相对简单,并且具有Druid连接池的强大功能,如监控、性能优化等。适用于多数据源、读写分离、多租户等复杂场景。

相关文章:

【Spring】DynamicDataSourceHolder 动态数据源切换

【Spring】DynamicDataSourceHolder 动态数据源切换 常见场景常见工具一、AbstractRoutingDataSource1.1、 定义 DynamicDataSourceHolder1.2、 配置动态数据源1.3、 在Spring配置中定义数据源1.4、在业务代码中切换数据源 二、Dynamic Datasource for Spring Boot2.1. 添加依赖…...

LeeCode 3165 线段树

题意 传送门 LeeCode 3165 不包含相邻元素的子序列的最大和 题解 考虑不含相邻子序列的最大和&#xff0c;在不带修改的情况下容易想到&#xff0c;以最后一个元素是否被选取为状态进行DP。从线性递推的角度难以处理待修改的情况。 从分治的角度考虑&#xff0c;使用线段树…...

修改元组元素

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 场景模拟&#xff1a;伊米咖啡馆&#xff0c;由于麝香猫咖啡需求量较大&#xff0c;库存不足&#xff0c;店长想把它换成拿铁咖啡。 实例08 将麝香猫…...

【模版方法设计模式】

文章目录 模板方法设计模式模板方法的设计原则模板方法设计模式组成部分代码实现抽象类实现具体实现类执行 模板方法设计模式 模版方法设计模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个操作中的算法骨架&#xff0c;而将一…...

rust语言初识

程序设计实践课上水一篇ing 来源&#xff1a;rust基础入门-1.初识rust-酷程网 (kucoding.com) rust作为一名新兴语言&#xff0c;与go又有些许不同&#xff0c;因为它的目标是对标系统级开发&#xff0c;也就是C、C这两位在编程界的位置。比如我们最常用的windows系统&#x…...

知识图谱数据预处理笔记

知识图谱数据预处理笔记 0. 引言1. 笔记1-1. \的转义1-2. 特殊符号的清理1-3. 检查结尾是否正常1-4. 检查<>是否存在1-5. 两端空格的清理1-6. 检查object内容长时是否以<开始 0. 引言 最近学习知识图谱&#xff0c;发现数据有很多问题&#xff0c;这篇笔记记录遇到的…...

Unity面试八股文之基础篇

文章目录 前言1. Unity的生命周期加载第一个场景Editor在第一次帧更新之前帧之间更新顺序协程销毁对象时退出时 2. Unity 协程和线程,进程的区别3. 本地坐标系 世界坐标系4. 碰撞器和触发器的区别后话 前言 开设这个栏目的博文会写一些有关unity的面试题目&#xff0c;在面试的…...

HTTPS能否避免流量劫持?如何实现HTTPS

在当今数字化时代&#xff0c;网站安全已经成为企业和个人的头等大事。随着网络犯罪和数据泄露的增加&#xff0c;保护您的网站免受潜在威胁比以往任何时候都更加重要。网站安全的一个关键组成部分是HTTPS&#xff0c;它代表着安全的超文本传输协议。HTTPS是标准HTTP协议的安全…...

簡述Vue 2.0 响应式数据的原理

Vue 2.0 响应式数据的原理主要基于以下几个关键点&#xff1a; 数据劫持与Object.defineProperty&#xff1a; Vue 2.0 使用 Object.defineProperty 方法来劫持对象的属性&#xff0c;为其添加 getter 和 setter 方法。当数据被访问或修改时&#xff0c;这些 getter 和 setter …...

Kafka线上集群部署方案怎么做?no.6

专栏前面几期内容&#xff0c;我分别从Kafka的定位、版本的变迁以及功能的演进等几个方面循序渐进地梳理了Apache Kafka的发展脉络。通过这些内容&#xff0c;我希望你能清晰地了解Kafka是用来做什么的&#xff0c;以及在实际生产环境中该如何选择Kafka版本&#xff0c;更快地帮…...

vscode 的 AI 协助插件 Tabnine / Codeium

4.1、Tabnine 描述&#xff1a;Tabnine 是一款基于深度学习技术的代码自动补全工具。该插件支持多种编程语言&#xff0c;包括 Python、JavaScript、TypeScript、Java 和 Go 等。它可以根据您输入的代码段和上下文信息&#xff0c;预测并推荐可能的代码补全选项&#xff0c;从而…...

Flutter 中的 OutlineButton 小部件:全面指南

Flutter 中的 OutlineButton 小部件&#xff1a;全面指南 在Flutter的Material Design组件库中&#xff0c;OutlineButton是一个用于创建带边框的扁平按钮的小部件。这种按钮通常用于次要操作或在需要强调其他按钮的情况下使用。本文将为您提供一个全面的指南&#xff0c;帮助…...

Kubernetes可视化界面之DashBoard

1.1 DashBoard Kubernetes Dashboard 是 Kubernetes 集群的一个开箱即用的 Web UI&#xff0c;提供了一种图形化的方式来管理和监视 Kubernetes 集群中的资源。它允许用户直接在浏览器中执行许多常见的 Kubernetes 管理任务&#xff0c;如部署应用、监控应用状态、执行故障排查…...

Docker学习(4):部署web项目

一、部署vue项目 在home目录下创建项目目录 将打包好的vue项目放入该目录下&#xff0c;dist是打包好的vue项目 在项目目录下&#xff0c;编辑default.conf 内容如下&#xff1a; server {listen 80;server_name localhost; # 修改为docker服务宿主机的iplocation / {r…...

驱动开发中引入私有数据的原因

系列文章目录 驱动开发中引入私有数据的原因 驱动开发中引入私有数据的原因 系列文章目录驱动开发中引入私有数据的原因 驱动开发中引入私有数据的原因 驱动开发中引入私有数据&#xff08;Private Data&#xff09;概念主要是为了解决以下几个关键问题&#xff1a; 1.多设备支…...

删除edge浏览器文本框储存记录值以及关闭自动填充

当我们点击输入框时总会出现许多以前输入过的信息。 一、删除edge浏览器文本框储存记录值 1、首先按下↓键选中你想删除的信息 二、关闭自动填充。 1、在地址栏输入edge://wallet/settings跳转到以下界面 2、往下滑找到 全部取消即可...

mysql事务 事务并发问题 隔离级别 以及原理

mysql事务 简介&#xff1a;事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 事务四大特性 原子性&#xff08;Atomici…...

Android 性能为王时代SparseArray和HashMap一争高下

文章目录 一、SparseArray 源码分析1. **类定义和构造函数**2. **基本方法**2.1 put(int key, E value)2.2 get(int key)2.3 delete(int key)2.4 removeAt(int index)2.5 gc()2.6 size()2.7 keyAt(int index) 和 valueAt(int index) 3. **辅助方法**3.1 binarySearch() 二、使用…...

学术图表的基本配色方法

不论是商业图表还是专业图表&#xff0c;图表的配色都极其关键。图表配色主要有彩色和黑白两种配色方案。刘万祥老师曾提出&#xff1a; “在我看来&#xff0c;普通图表与专业图表的差别&#xff0c;很大程度就体现在颜色运用上。” 对于科学图表&#xff0c;大部分国内的期…...

【学习笔记】Webpack5(Ⅱ)

Webpack 3、高级篇 3.1、提升开发体验 —— SourceMap 3.2、提升打包速度 3.2.1 HotModuleReplacement 3.2.2 OneOf 3.2.3 Include / Exclude 3.2.4 Cache 3.2.5 Thread 3.3、减少代码体积 …...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

Golang——9、反射和文件操作

反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一&#xff1a;使用Read()读取文件2.3、方式二&#xff1a;bufio读取文件2.4、方式三&#xff1a;os.ReadFile读取2.5、写…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)

引言 工欲善其事&#xff0c;必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后&#xff0c;我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集&#xff0c;就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...

华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)

题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...