【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连接池的强大功能,如监控、性能优化等。适用于多数据源、读写分离、多租户等复杂场景。

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

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

相关文章

Element plus 低版本弹窗组件添加拖拽功能

在使用element plus 弹窗组件el-dialog 的时候&#xff0c;由于自己组件库版本过低&#xff0c;所以就会缺失某些功能&#xff0c;比如弹窗组件的可拖拽功能。因为某些原因element plus 组件库又不能升级&#xff0c;所以此时就需要自己为弹窗组件添加拖拽功能。共分为一下四个…

斯坦福大学李飞飞教授分享:只有计算机和机器人具备空间智能,人工智能的潜力才能得到充分发挥

在最近李飞飞教授的TED演讲中&#xff0c;她深入探讨了空间智能对人工智能&#xff08;AI&#xff09;发展的深远影响。 今天&#xff0c;AI的发展也在经历类似的革命性变化。李飞飞教授回顾了计算机视觉的早期进展&#xff0c;介绍了她和团队在ImageNet上的努力&#xff0c;以…

英语学习笔记24——Give me/us/him/her/them some ...

Give me/us/him/her/them some … 给我/我们/他/她/他们一些…… 词汇 Vocabulary desk n. 课桌&#xff08;有书桌堂&#xff09;&#xff0c;写字台 复数&#xff1a;desks 搭配&#xff1a;desk mate 同桌    构成&#xff1a;desk mate 桌子上的伙伴 同桌    cl…

[Algorithm][动态规划][简单多状态DP问题][买卖股票的最佳时机 III][买卖股票的最佳时机 Ⅳ]详细讲解

目录 1.买卖股票的最佳时机 III1.题目链接2.算法原理详解3.代码实现 2.买卖股票的最佳时机 IV1.题目链接2.算法原理详解3.代码实现 1.买卖股票的最佳时机 III 1.题目链接 买卖股票的最佳时机 III 2.算法原理详解 注意&#xff1a;本题为了便于初始化&#xff0c;有较多细节服…

Ubuntu开发入门之“制作Ubuntu rootfs根文件系统镜像“

Ubuntu开发入门之“制作Ubuntu rootfs根文件系统镜像” 问题描述解决方法1.首先从官网下载最基础的ubuntu base核心文件,ubuntu core.2.接下来就是制作一个基础功能的根文件系统3.修改可用源4.接下来就是挂载根文件系统,进行模拟安装应用5.根文件系统安装常用的工具和配置用户…

Polar Si9000 远程桌面提示cannot checkout an uncounted license错误

Polar Si 9000 版本《Si9000e_2022_v22_03》安装完成之后指定到license时候提示如下&#xff1a; cannot checkout an uncounted license 修正办法将安装目录下的license用文档编辑工具&#xff08;记事本或者Notepad&#xff09;打开.lic文件&#xff0c;使用编辑器的替换功…

瑞萨RA8系列教程 | 基于e2s实现RA8串口输出配置

关注星标公众号&#xff0c;不错过精彩内容 作者 | strongerHuang 微信公众号 | strongerHuang 串口是最常见的通信方式之一&#xff0c;也是单片机调试最常见的通信接口&#xff0c;也是现在的单片机必备的通信接口&#xff0c;目前&#xff08;2024-05&#xff09;最新、最强…

【香橙派AIpro】开箱测评

1.板子开箱 哟&#xff0c;看起来还不错哦&#xff01;&#xff01;&#xff01; 收货清单&#xff1a; 主板*1 1.5m数据线*1 充电头*1 1.1.充电头 近65W的充电头&#xff0c;不错不错。 1.2.主板 1.2.1.上面 哇噢&#xff0c;还送了2.4/5G的WiFi和蓝牙天线。 emm&#xf…

卷出新高度,直呼太强!时隔三月,YOLO再度进化升级:《YOLOv10—实时端到端目标检测》重磅来袭

真的是不止一次感叹&#xff0c;学习的速度都跟不上发论文出新品的速度。。。。。 继前文YOLOv9发布以来也就不到三个月的时间&#xff0c;YOLOv10就来了&#xff01; 《太卷了&#xff0c;目标检测新成员——YOLOv9: Learning What You Want to LearnUsing Programmable Gra…

吴恩达2022机器学习专项课程C2W2:2.22 多类 softmax softmax与神经网络 softmax的代码改良 多标签分类

目录 多分类问题1.什么是多分类问题2.多分类问题案例3.二分类与多分类的区别 Softmax1. 什么是Softmax2.逻辑回归预测的计算过程3. Softmax预测的计算过程4.Softmax 回归与逻辑回归的关系5. Softmax的损失函数 softmax与神经网络1.设置Softmax层2.Softmax层的计算3.softmax激活…

卸载/删除 Maxask.com,最简单的方法

被绑架的浏览器&#xff0c;太恶心了。 Maxask伪装成了插件&#xff0c;在你搜索网页的时候利用了重定向&#xff0c;导致出现的界面时Maxask的界面&#xff0c;很恶心。 只需要排查正在使用的&#xff0c;如下图有颜色的图表。 删除一个插件&#xff0c;浏览器搜索一下看看有…

先进制造aps专题九 中国aps行业分析

国外aps的问题是不给国内客户定制算法 国外aps的算法都很强大&#xff0c;考虑几百个约束条件&#xff0c;各种复杂的工序关系&#xff0c;还有副资源约束特殊规格约束&#xff0c;排程还优化&#xff0c;光c写的算法代码就几十万行甚至上百万行 国内aps的问题是实现不了复杂的…

项目范围管理

目录 1.概述 2.主要工作 3.基础 4.项目范围管理的过程 5.规划范围管理 6.收集需求 7.定义范围 8.创建 WBS 9.确认范围 10.控制范围 1.概述 项目范围管理是项目管理中的一个重要组成部分&#xff0c;涉及到确定项目需要完成的工作范围&#xff0c;以及如何管理和控制…

String到底创建几个对象?

String在生成的过程中如何创建对象&#xff1f; String s1 new String("hello") String s2 "world" String s3 new String("x") new String("y") String s5 new String("abc") "def" String s6 new String…

mysql数据库安装指南

这里写自定义目录标题 官网下载mysql数据库安装MySQL数据库添加环境变量查看mysql的服务验证是否配置成功 注意&#xff1a;如果用MySQL8的版本安装不成功&#xff0c;可以尝试用MySQL5的版本。MySQL8的版本可能在windows上一直不能启动&#xff0c;经过查询是某个版本的漏洞。…

分布式系列之分布式锁

背景 SOA或微服务架构体系下必不可少的一个分布式组件&#xff0c;常用于解决分布式场景下数据一致性的问题。 应用场景&#xff1a; 资源竞争控制&#xff1a;在分布式系统中&#xff0c;多个节点可能同时访问共享资源&#xff0c;如数据库、文件系统、缓存等。分布式锁可以…

如何在 jQuery 中检查一个元素是否隐藏

在前端开发中&#xff0c;我们常常需要判断一个元素是否隐藏。在 jQuery 中&#xff0c;有多种方式可以实现这一点。然而&#xff0c;并不是所有的解决方案都适用于所有情况。为此&#xff0c;我写了一个通用的解决方案来应对各种复杂情形。本文将详细介绍这个方法及其使用方式…

知识存储概述

文章目录 知识存储概述知识存储方式知识存储基础工具技术发展趋势 知识存储是针对知识图谱的知识表示形式设计底层存储方式&#xff0c;完成各类知识的存储&#xff0c;以支持对大规模图数据的有效管理和计算。知识存储的对象包括基本属性知识、关联知识、事件知识、时序知识和…

操作系统 实验17 批处理操作接口7:until循环与select循环

1、建立文件Until.sh实现累加和 脚本&#xff1a; #!/bin/bash sum010 sum020 i1 until [ $i -gt 100 ] dolet "sum01i"let "ji%2"if [ $j -ne 0 ];thenlet "sum02i"filet "i1" done echo $sum01 echo $sum02命令&#xff1a;. ./Un…

深入解析 RocketMQ 和 Kafka 的消息压缩机制

深入解析 RocketMQ 和 Kafka 的消息压缩机制 消息队列系统在现代分布式系统中扮演着重要角色&#xff0c;它们不仅需要高效地传递消息&#xff0c;还需要在传输过程中尽量减少带宽和存储的占用。消息压缩是一种常见的优化手段&#xff0c;可以显著减少消息的体积。本文将详细探…