一种数据源切换的实践方案

news2025/1/12 1:01:50

随着业务的不断深入,我们会碰见很多关于数据源切换的业务场景,数据源切换也是当前最常用的分库后的分流策略方式之一,对于读写职责分离的数据库集群而言,我们在服务层面制定相应的接口与数据库交互的定制化开发,也就是我们可以自己通过程序控制当前的接口访问的数据库的地址与数据源的实例对象。对于同一个业务系统使用到不同服务器的数据库而言,我们也可以动态的切换数据源,从而做到预先准备,用时切换的功能。当然数据源切换也只是一种解决方案,并不是最合适的实践思路,本文旨在与大家分享常用的数据源切换的实践思路。

AbstractRoutingDataSource核心抽象类

字面意思上,抽象路由数据源,其中的源码如下:

核心属性与实现的核心拓展接口InitializingBean(属性填充之前实例化之前)

 核心的某些设置目标数据源方法(透传给容器,或感知到容器的某些特性)

 下面就是重写Initializing接口的实现方法afterProptriesSet()属性填充之后的bean的自定义逻辑

 核心的切换数据源的方法,以及可以从中看出如何获取数据源的方法

 那么我们看完源码大概能了解到determineCurrentLookupKey方法就是我们核心的修改数据源的方法,只要我们在这个方法的实现里稍微修改一下KEY的返回逻辑,是不是就能够起到切换数据源的逻辑?

由此我们设计一个自定义的动态数据源类:

@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDynamicDataSourceKey();
    }
}

这里的DynamicDataSourceHolder类主要的作用是利用ThreadLocal<String>维护一个请求自己的数据源信息:

@Slf4j
public class DynamicDataSourceHolder {

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

    public static void setDynamicDataSourceKey(String key){
        log.info("数据源切换为:{}",key);
        DYNAMIC_DATASOURCE_KEY.set(key);
    }

    public static String getDynamicDataSourceKey(){
        String key = DYNAMIC_DATASOURCE_KEY.get();
        return key == null ? DataSourceType.WRITE: key;
    }

    public static void removeDynamicDataSourceKey(){
        log.info("移除数据源:{}",DYNAMIC_DATASOURCE_KEY.get());
        DYNAMIC_DATASOURCE_KEY.remove();
    }
}

那么切换的逻辑已经基本都交给容器去处理了,我们接下来要实现读取配置文件中的多数据源设置参数,并将这些数据源实例化后交给容器管理。

由此我们设计一个DruidDataSourceConfiguration:

@Configuration
@ConditionalOnClass(DruidDataSource.class)
@ConditionalOnProperty(
        name = "spring.datasource.druid.write",
        matchIfMissing = true
)
public class DataSourceConfiguration {
    @Bean(name= DataSourceType.WRITE)
    @Qualifier(DataSourceType.WRITE)
    @ConfigurationProperties("spring.datasource.druid.write")
    public DruidDataSource wirteDruidDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name= DataSourceType.READ)
    @Qualifier(DataSourceType.READ)
    @ConfigurationProperties("spring.datasource.druid.read")
    public DruidDataSource readDruidDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public DynamicDataSource dynamicDataSource()
    {
        Map<Object, Object> dataSourceMap = new HashMap<>(2);
        dataSourceMap.put(DataSourceType.WRITE,wirteDruidDataSource());
        dataSourceMap.put(DataSourceType.READ,readDruidDataSource());
        //设置动态数据源
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(wirteDruidDataSource());
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        return dynamicDataSource;
    }
}

这里主要的调用也是在之前的源码里有提到,我们还定义了一个所谓的接口:

public interface DataSourceType{

    String WRITE = "WRITE";
    String READ = "READ";

}

接下来我们还要利用注解规定某一个接口是否需要切换数据源:

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    /*数据源类型*/
    String value() default DataSourceType.READ;
}

通常最常用的方式就是aop去代理接口所属的对象,拿到代理的数据信息进行处理,这里我们是不是可以切换当前数据源到我们注解上过规定的数据源呢?

@Aspect
@Slf4j
@Component
public class DataSourceAspect {

    @Pointcut("@annotation(com.runjing.learn_runjing.dataSource.DataSource)")
    public void point(){}

    @Around("point()")
    public void setDataSource(ProceedingJoinPoint pointcut) throws Throwable{
        String value = getDefineAnnotation(pointcut).value();
        DynamicDataSourceHolder.setDynamicDataSourceKey(value);
        try{
            pointcut.proceed();
        }finally {
            DynamicDataSourceHolder.removeDynamicDataSourceKey();
        }
    }

    private DataSource getDefineAnnotation(ProceedingJoinPoint joinPoint){
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        DataSource dataSourceAnnotation = methodSignature.getMethod().getAnnotation(DataSource.class);
        if (Objects.nonNull(methodSignature)) {
            return dataSourceAnnotation;
        } else {
            Class<?> dsClass = joinPoint.getTarget().getClass();
            return dsClass.getAnnotation(DataSource.class);
        }
    }

}

以上,一种动态数据源的切换实践方案分享结束。

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

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

相关文章

云 cloud 高可用系统--在RDS上实现,从原理上不可能保证你100%不丢数据

我写这篇文字&#xff0c;实属无奈&#xff0c;在目前很多企业都依赖云的情况下&#xff0c;数据库的很多事情都是身不由己&#xff0c;发生问题&#xff0c;你查看日志&#xff0c;分析日志可能你连日志都不是全部的&#xff0c;并且想通过程序来过滤这个日志很多情况下都有限…

数据库系统概述——第六章 关系数据理论(知识点复习+练习题)

&#x1f31f;博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;离散数学考前复习&#xff08;知识点题&#xff09; &#x1f353;专栏&#xff1a;概率论期末速成&#xff08;一套卷&#xff09; &#x1f433;专栏&#xff1a;数字电路考前复习 &#x1f99a;专栏&am…

CMU 15-445 Project #2 - B+Tree(CHECKPOINT #1)

CHECKPOINT #1 一、题目链接二、准备工作三、部分实现1.查找操作2.插入操作3.删除操作 四、评测结果 一、题目链接 二、准备工作 见 CMU 15-445 Project #0 - C Primer 中的准备工作。 三、部分实现 对于B树的节点定义&#xff0c;通过节点类的命名 b_plus_tree_page 不难发现…

linux-centos7操作系统查看系统未挂载的磁盘,挂载磁盘

linux-centos7操作系统查看系统未挂载的磁盘,挂载磁盘 查看当前磁盘空间 根目录 / 下也只有44G,其他目录只有10几G,正式环境肯定不够用 df -h查看硬盘数量和分区情况 fdisk -l查看到/dev/vdb 有500多G了 将/dev/vdb在分出一个区使用 第一步:编辑分区。执行命令fdisk …

pr视频叠加,即原视频右上角添加另外一个视频方法,以及pr导出视频步骤

一、pr视频叠加&#xff0c;即原视频右上角添加另外一个视频方法 在使用pr制作视频时&#xff0c;我们希望在原视频的左上角或右上角同步播放另外一个视频&#xff0c;如下图所示&#xff1a; 具体方法为&#xff1a; 1、导入原视频&#xff0c;第一个放在v1位置&#xff0c;第…

Selenium编写自动化用例的8种技巧

在开始自动化时&#xff0c;您可能会遇到各种可能包含在自动化代码中的方法&#xff0c;技术&#xff0c;框架和工具。有时&#xff0c;与提供更好的灵活性或解决问题的更好方法相比&#xff0c;这种多功能性导致代码更加复杂。在编写自动化代码时&#xff0c;重要的是我们能够…

【序列dp】最长上升子序列(一)

文章目录 最长上升子序列-序列dp概览895 最长上升子序列-O(n^2)1017 怪盗基德的滑翔翼1014 登山482 合唱队形1012 友好城市 最长上升子序列-序列dp 什么是序列相关的 DP &#xff1f;序列相关 DP&#xff0c;顾名思义&#xff0c;就是将动态规划算法用于数组或者字符串上&…

textgen教程(持续更新ing...)

诸神缄默不语-个人CSDN博文目录 官方GitHub项目&#xff1a;shibing624/textgen: TextGen: Implementation of Text Generation models, include LLaMA, BLOOM, GPT2, BART, T5, SongNet and so on. 文本生成模型&#xff0c;实现了包括LLaMA&#xff0c;ChatGLM&#xff0c;B…

C++课程学习记录

目录 1. 前置说明2. 二叉树的模拟2.1 参考资料2.2 二叉树的构建2.2.1 递归构建2.2.2 迭代构建 2.3 二叉树的遍历2.4 二叉树的应用 3. 继承与派生3.1 最简单的生死3.2 动态申请空间的生死3.3 继承中的protectd权限3.4 三种继承方式3.5 修改某些继承成员的继承类型3.6 多级派生3.…

C++57个入门知识点_番外1_C++指针偏移在类中的应用及指针偏移原理

这是对C指针偏移介绍比较好的博文&#xff0c;但是比较分散&#xff0c;我把其进行了整理&#xff0c;原博文地址请见最后&#xff0c;讲的很详细。 C57个入门知识点_番外1_C指针偏移在类中的应用及指针偏移原理 1. C指针偏移原理2. C显示十进制内存地址&#xff08;不用理解&…

AQS原理

目录 一、原理概述二、AQS 对资源的共享方式三、AQS底层使用了模板方法模式四、使用demo&#xff0c;使用AQS实现不可重入锁五、AQS使用到的几个框架 一、原理概述 AQS全称是 AbstractQueuedSynchronizer&#xff0c;是阻塞式锁和相关的同步器工具的框架 AQS核心思想是&#…

Appian低代码平台

国外老牌低代码开发平台Appian Appian在国内用的比较少&#xff0c;资料也很匮乏。需要自己主动去官网寻找。 Appian 学习平台 进入Appian Community可以选择学习路径&#xff0c;可以选择适合自己的学习路径&#xff1b;我选择的是Builder路径&#xff0c; 看了足足80个小…

opencv检测二维码和条形码

文章目录 1 excel制作简单二维码2 识别二维码和条形码2.1 相关库2.2 decode解码2.3 圈出二维码的位置2.4 判断二维码是否授权 3 完整代码3.1 使用图片进行识别3.2 使用摄像头实时识别 4 总结 1 excel制作简单二维码 使用excel可以实现制作二维码&#xff0c;但只能实现做英文和…

基于51单片机的简易电子琴设计

目录 摘 要 基于51单片机的简易电子琴设计 一、系统设计 1、项目概要 2.设计任务和基本要求 二、硬件设计 1、硬件设计概要 2、时钟振荡电路模块 3.复位电路模块 5.数码管电路模块 6.蜂鸣器模块 7、乐曲切换电路模块 三、软件原理 四、软件流程图 五、代码实现 …

解析Transformer基本结构与实现

1.基本结构 ​ Transformer总体架构可分为4个部分&#xff1a; 输入部分-输出部分-编码器部分-解码器部分 输入部分包含&#xff1a; 原文本嵌入层&#xff08;Input embedding&#xff09;及其位置编码(position encoding)目标文本嵌入层及其位置编码器 文本嵌入层的作…

TCP/UDP协议重温三次握手四次挥手 简单笔记

术语储备&#xff1a; SYN&#xff1a;同步位 &#xff1b;SYN1,表示进行一个连接请求 ACK&#xff1a;确认位 &#xff1b;ACK1,确认有效 ACK0&#xff0c;确认无效 ack : 确认号 &#xff1b;对方发送序号1 seq &#xff1a; 序号 ; 标识从TCP发端向TCP收端发送的数据字节流 …

基于JPA的Repository使用详解

Spring Data JPA Spring Data是Spring提供的操作数据的框架&#xff0c;Spring Data JPA是Spring Data的一个模块&#xff0c;通过Spring data 基于jpa标准操作数据的模块。 Spring Data的核心能力&#xff0c;就是基于JPA操作数据&#xff0c;并且可以简化操作持久层的代码。…

BLOND:ISH VoxEdit 创作大赛来啦!

准备好随着 BLOND:ISH 的节拍释放你们的创造力和节奏&#xff0c;因为我们将举办一场与众不同的刺激比赛。你们可以在 BLOND:ISH VoxEdit 大赛中展示你们的才华并赢得 SAND 奖励&#xff01; &#x1f3dd;️ 比赛主题&#xff1a;ABRA 夏日派对 &#x1f3dd;️ 释放你们的想象…

【Python学习】—Python基础语法(二)

文章目录 【Python学习】—Python基础语法&#xff08;二&#xff09;一、字面量二、注释三、变量四、数据类型五、数据类型转换六、标识符七、运算符八、字符串扩展九、获取键盘输入 【Python学习】—Python基础语法&#xff08;二&#xff09; 一、字面量 字面量&#xff1…

文件类型识别的实现思路

一些网络设备&#xff0c;比如防火墙或者审计系统&#xff0c;一般都有文件过滤的功能&#xff0c;可以对用户上网传输的文件进行过滤&#xff0c;比如可以限制用户通过ftp下载word文档&#xff0c;也就是文件类型为doc或者docx的文件。 那么文件过滤的功能是怎么实现呢&#…