Spring中实现动态数据源切换,基于AbstractRoutingDataSource

news2024/11/28 13:45:24

背景

在项目开发过程中,我们可能会遇到一个场景:某个类型数据源有多个数据源实例,需要我们按照不同的请求切换到不同数据源去。
而目前绝大多数java应用都是基于Spring框架来开发,我们很多时候相关的数据源连接都是交给了Spring框架去管理,这就需要Spring能够支持动态数据源切换。

方案

Spring中预留了这个接口,通过AbstractRoutingDataSource能够动态切换数据源。

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {

这是一个抽象类,预留了一个抽象方法:

protected abstract Object determineCurrentLookupKey();

我们知道,数据源一般都会提供一个getConnection方法来获取一个连接,在AbstractRoutingDataSource 实现如下:

	@Override
	public Connection getConnection() throws SQLException {
		return determineTargetDataSource().getConnection();
	}
	protected DataSource determineTargetDataSource() {
		Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
		Object lookupKey = determineCurrentLookupKey();
		DataSource dataSource = this.resolvedDataSources.get(lookupKey);
		if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
			dataSource = this.resolvedDefaultDataSource;
		}
		if (dataSource == null) {
			throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
		}
		return dataSource;
	}

可以看到,AbstractRoutingDataSource 获取连接的主要逻辑就是通过determineCurrentLookupKey获取到一个数据源的关联key,然后从resolvedDataSources中去获取。
resolvedDataSources的初始化,则放在afterPropertiesSet中:

	@Override
	public void afterPropertiesSet() {
		if (this.targetDataSources == null) {
			throw new IllegalArgumentException("Property 'targetDataSources' is required");
		}
		this.resolvedDataSources = CollectionUtils.newHashMap(this.targetDataSources.size());
		this.targetDataSources.forEach((key, value) -> {
			Object lookupKey = resolveSpecifiedLookupKey(key);
			DataSource dataSource = resolveSpecifiedDataSource(value);
			this.resolvedDataSources.put(lookupKey, dataSource);
		});
		if (this.defaultTargetDataSource != null) {
			this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
		}
	}

这里起始就是通过targetDataSources中指定的数据源复制到resolvedDataSources 中去。因此如果多数源是固定的,那么只需要实现determineCurrentLookupKey方法即可。但是如果多数据源不固定,比如可能会有数据源的变更,那么这种实现是不能够支持,因为这种实现从服务启动的视乎,后续数据源就不能发生变更,这需要我们自己实现determineTargetDataSource.
下面是一个参考实现:

public class DataSourceContextHolder {

    private static final ThreadLocal<String> DATASOURCE_CONTEXT_KEY_HOLDER = new ThreadLocal<>();


    public static void switchDataSource(String key){
        log.info("Switch to data source:" + key);
        DATASOURCE_CONTEXT_KEY_HOLDER.set(key);
    }
    public static String getDataSourceKey(){
        return DATASOURCE_CONTEXT_KEY_HOLDER.get() ;
    }

}

public class DynamicDataSource extends AbstractRoutingDataSource {

    private Map<Object, Object> targetDataSources = new HashMap<>();
    private Map<Object, DataSource> dataSources = new HashMap<>();
    public DynamicDataSource (){
        super.setDefaultTargetDataSource(null);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }
 @Override
    protected DataSource determineTargetDataSource() {
        Object dataSourceKey = determineCurrentLookupKey();
        return dataSources.get(dataSourceKey);
    }
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder .getDataSourceKey();
    }
    public synchronized void addDataSource(String key, DataSource dataSource){
        targetDataSources.put(key,dataSource);
        dataSources.put(key,dataSource);
        log.info("add tenant dynamic dataSource for tenantId = {} ",key);
    }
}

这样我们通过DataSourceContextHolder 来调整当前线程关联的数据源。

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

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

相关文章

云技术-docker

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团…

《解锁计算机专业宝藏:核心编程语言与学习资料全解析》

在当今数字化浪潮汹涌澎湃、技术迭代日新月异的时代&#xff0c;计算机专业宛如一座蕴藏无尽宝藏与无限机遇的神秘殿堂&#x1f3f0;。对于莘莘学子而言&#xff0c;精准掌握核心编程语言&#xff0c;并手握优质学习资料&#xff0c;恰似寻得开启这扇殿堂大门的秘钥&#xff0c…

27加餐篇:gRPC框架的优势与不足之处

gRPC作为一个现代的、开源的远程过程调用(RPC)框架,在多个方面都展现了其优雅之处,同时也存在一些不足之处。这篇文章我们就相对全面的分析一下gRPC框架那些优雅的地方和不足的地方。 优雅的地方 gRPC作为一个RPC框架,在编码、传输协议已经支持多语言方面都比较高效,下…

Leetcode打卡:交替组II

执行结果&#xff1a;通过 题目&#xff1a;3208 交替组II 给你一个整数数组 colors 和一个整数 k &#xff0c;colors表示一个由红色和蓝色瓷砖组成的环&#xff0c;第 i 块瓷砖的颜色为 colors[i] &#xff1a; colors[i] 0 表示第 i 块瓷砖的颜色是 红色 。colors[i] 1 …

在Windows下编译支持https的wsdl2h

下载源码 在官网下载源码 安装Openssl 下载OpenSSL并安装&#xff0c;安装完成后需要将OpenSSL的路径添加到环境变量中 配置VS 1、打开工程 2、因为前面安装的OpenSLL是64位的&#xff0c;因此需要创建一个X64的配置 打开配置管理器&#xff0c;然后选择新建&#xff0…

利用Prompt工程为LLM提升推理能力

利用Prompt工程为LLM提升推理能力 基于策略的推理详解ReAct: 推理与行动思维链&#xff1a;逐步解决问题反思&#xff1a;深入分析和自我审查与代理架构的集成实际应用代码附录 众所周知&#xff0c;一个精心设计的Prompt能够显著增强大型语言模型&#xff08;LLMs&#xff09;…

[C++ 核心编程]笔记 4.1 封装

4.1.1 封装的意义 封装是C面向对象三大特性之一 封装的意义: 将属性和行为作为一个整体&#xff0c;表现生活中的事物将属性和行为加以权限控制 封装意义一: 在设计类的时候&#xff0c;属性和行为写在一起&#xff0c;表现事物 语法: class 类名{ 访问权限: 属性 /行为 }…

基于Qt实现的自定义树结构容器:设计与应用

在Qt框架中&#xff0c;尽管其提供了许多强大的容器类&#xff08;如 QList, QMap, QTreeWidget 等&#xff09;&#xff0c;但缺少一个通用的、灵活的树结构容器&#xff0c;直接支持多层级数据管理。为了满足这些需求&#xff0c;本文设计并实现了一个可复用的自定义树结构容…

Web登录页面设计

记录第一个前端界面&#xff0c;暑假期间写的&#xff0c;用了Lottie动画和canvas标签做动画&#xff0c;登录和注册也连接了数据库。 图片是从网上找的&#xff0c;如有侵权私信我删除&#xff0c;谢谢啦~

钟睒睒的“傲慢与偏见”

文章内容根据网络内容整理形成 最近&#xff0c;钟睒睒关于绿瓶水不适合长期饮用的言论&#xff0c;在网上引起了一番新的热议&#xff0c;让刚平静不久的包装饮用水竞争&#xff0c;再次掀起一阵波澜&#xff0c;同时&#xff0c;其对于企业家直播带货的等系列看法&#xff0c…

【ArcGISPro】使用AI提取要素-土地分类(sentinel2)

Sentinel2数据处理 【ArcGISPro】Sentinel-2数据处理-CSDN博客 土地覆盖类型分类 处理结果

【技术文档:技术传播的灯塔】

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Qt-系统相关(2)多线程网络

Qt多线程 在 Qt 中&#xff0c;多线程的处理⼀般是通过 QThread类 来实现。 QThread 代表⼀个在应⽤程序中可以独⽴控制的线程&#xff0c;也可以和进程中的其他线程共享数据。 QThread 对象管理程序中的⼀个控制线程。 QThread 常⽤ API&#xff1a; 使用线程 关于创建线程…

永久免费使用最好的Markdown编辑器——Typora

介绍 Typora 是一款轻量级的 Markdown 编辑器&#xff0c;其最为出众的特点是&#xff1a; 所见即所得。 Typora 于2021年11月23日推出了第一个正式版&#xff0c;并转为收费。不过价格也算合理&#xff0c;89元/3台设备&#xff0c;为一次买断制。 直到2022年年中&#xff0…

Linux环境实现c语言编程

Linux环境实现c语言编程 准备工作 准备一台Linux虚拟机/机器 操作流程 打开Linux机器 打开终端 查看是否有GCC编译器 GCC是什么 GCC是GNU编译器集合&#xff08;GNU Compiler Collection&#xff09;的简称。它是一套免费的开源编程语言编译器&#xff0c;支持多种编程语…

VTK中对于相机camera的设置

1. 相机的核心属性 在 VTK 中&#xff0c;vtkCamera 的核心属性有默认值。如果你不设置这些属性&#xff0c;相机会使用默认值来渲染场景。 Position&#xff08;默认值&#xff1a;(0, 0, 1)&#xff09;&#xff1a; 默认情况下&#xff0c;相机位于 Z 轴正方向的 (0, 0, 1)…

glog在vs2022 hello world中使用

准备工作 设置dns为阿里云dns 223.5.5.5&#xff0c;下载cmake&#xff0c;vs2022&#xff0c;git git clone https://github.com/google/glog.git cd glog mkdir build cd build cmake .. 拷贝文件 新建hello world并设置 设置预处理器增加GLOG_USE_GLOG_EXPORT;GLOG_NO_AB…

Python开发环境搭建+conda管理环境

下载Miniconda 推荐从清华镜像下载安装包 Index of /anaconda/miniconda/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 打开网页后&#xff0c;下拉到最后找到Miniconda3-latest前缀的文件&#xff0c;或者网页中直接搜索Miniconda3-latest&#xff0c;都可以找…

深度学习:GPT-2的MindSpore实践

GPT-2简介 GPT-2是一个由OpenAI于2019年提出的自回归语言模型。与GPT-1相比&#xff0c;仍基于Transformer Decoder架构&#xff0c;但是做出了一定改进。 模型规格上&#xff1a; GPT-1有117M参数&#xff0c;为下游微调任务提供预训练模型。 GPT-2显著增加了模型规模&…

C++设计模式-策略模式-StrategyMethod

动机&#xff08;Motivation&#xff09; 在软件构建过程中&#xff0c;某些对象使用的算法可能多种多样&#xff0c;经常改变&#xff0c;如果将这些算法都编码到对象中&#xff0c;将会使对象变得异常复杂&#xff1b;而且有时候支持不使用的算法也是一个性能负担。 如何在运…