SpringBoot配置动态数据源

news2024/9/21 12:37:00

1.  数据源准备

1.1 创建配置文件 application.yaml

spring:
  datasource:
    master:
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 123456
      jdbc-url: jdbc:mysql://localhost:3306/master?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&allowMultiQueries=true
    slave:
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 123456
      jdbc-url: jdbc:mysql://localhost:3306/slave?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&allowMultiQueries=true

1.2 数据库数据

存在两个库 masterslave,每个库中都有一个 route 表,每个表中有一条数据,其中 sign 为库名

2. 相关代码

2.1 创建实体类 DynamicDataSourceContextHolder

public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    public static void set(String dataSource) {
        CONTEXT_HOLDER.set(dataSource);
    }

    public static String get() {
        return CONTEXT_HOLDER.get();
    }

    public static void remove() {
        CONTEXT_HOLDER.remove();
    }
    
}

用于获取设置移除数据源

2.2 创建实体类 DynamicDataSource

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.get();
    }
}

DynamicDataSource 继承自 AbstractRoutingDataSource,AbstractRoutingDataSource 的 getConnection 方法会通过调用 determineTargetDataSource 方法推断数据源

2.3 创建枚举类 DataSourceEnum 

public enum DataSourceEnum {

    MASTER("master"),

    SLAVE("slave");

    private final String value;

    public String getValue() {
        return value;
    }

    DataSourceEnum(String value) {
        this.value = value;
    }
}

2.4 创建配置类 DataSourceConfig

@Configuration
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean
    @Primary
    public DynamicDataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                               @Qualifier("slaveDataSource") DataSource slaveDataSource) {

        Map<Object, Object> dataSourceMap = new HashMap<>();

        dataSourceMap.put(DataSourceEnum.MASTER.getValue(), masterDataSource);
        dataSourceMap.put(DataSourceEnum.SLAVE.getValue(), slaveDataSource);

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
        dynamicDataSource.setTargetDataSources(dataSourceMap);

        return dynamicDataSource;
    }

}
2.4.1 注意点
  1. masterDataSource()、slaveDataSource() 方法的返回值的类型是 HikariDataSource,HikariDataSource 只存在属性 jdbcUrl,不存在属性 url,所以配置文件 application.yaml 的相关属性要改成 jdbcUrl
  2. dynamicDataSource() 方法需要添加 @Primary 注解,因为当前环境中存在3个类型为 DataSource 的 bean,这样可以让其他依赖 DataSource 的 bean 优先选择 DynamicDataSource,然后再通过 DynamicDataSource 的 determineCurrentLookupKey 方法决定实际使用的数据源

3. 自定义注解用于切换数据源

3.1 创建自定义注解 DataSourceRoute

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceRoute {

    DataSourceEnum value() default DataSourceEnum.MASTER;
}

3.2 创建切面 DataSourceRouteAspect

@Component
@Aspect
public class DataSourceRouteAspect {

    @Pointcut("@annotation(com.ys.blog.annotation.DataSourceRoute)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {

        MethodSignature ms = (MethodSignature) pjp.getSignature();

        Method method = pjp.getTarget().getClass().getMethod(ms.getName(), ms.getParameterTypes());

        if (method.isAnnotationPresent(DataSourceRoute.class)) {
            DataSourceRoute dataSourceRoute = method.getAnnotation(DataSourceRoute.class);
            DataSourceEnum dataSourceEnum = dataSourceRoute.value();
            DynamicDataSourceContextHolder.set(dataSourceEnum.getValue());
        }

        return pjp.proceed(pjp.getArgs());
    }
}

4. 引入 mybatis 

4.1 创建 RouteMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ys.blog.mapper.RouteMapper">

    <select id="getSign" resultType="java.lang.String">
        select sign from route where id = 1
    </select>

</mapper>

 4.2 创建接口 RouteMapper

public interface RouteMapper {
    String getSign();
}

5. 其他

5.1 创建 RouteController

@RestController
@RequestMapping("/route")
public class RouteController {

    @Autowired
    private RouteMapper routeMapper;

    @GetMapping("/master")
    @DataSourceRoute(DataSourceEnum.MASTER)
    public String masterRoute() {
        return routeMapper.getSign();
    }

    @GetMapping("/slave")
    @DataSourceRoute(DataSourceEnum.SLAVE)
    public String slaveRoute() {
        return routeMapper.getSign();
    }
}

5.2 创建启动类 BlogApplication

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableConfigurationProperties
@MapperScan(value = "com.ys.blog.mapper")
public class BlogApplication {

    public static void main(String[] args) {
        SpringApplication.run(BlogApplication.class, args);
    }
}
5.2.1 注意点

注解 @SpringBootApplication 需要移除自动配置类 DataSourceAutoConfiguration,不然会导致循环依赖

5.2.2 原因简述

DataSourceAutoConfiguration 会导入一个类型为 DataSourceInitializerPostProcessor 的 BeanPostProcessor。如果 bean 的类型是 DataSource,会实例化一个类型为 DataSourceInitializerInvoker 的 bean,DataSourceInitializerInvoker 的构造方法依赖 DataSource

综上所述:DynamicDataSource 的实例化会触发 masterDataSource() 方法执行,masterDataSource() 方法的返回值类型是 DataSource,所以又会触发 DataSourceInitializerInvoker 的实例化,DataSourceInitializerInvoker 的实例化又依赖一个类型为 DataSource 的 bean,所以就产生了循环依赖

6. 测试

6.1 访问 master 数据源

通过运行结果,得出结论:访问的是 master 数据源

6.2 访问 slave 数据源

通过运行结果,得出结论:访问的是 slave 数据源

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

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

相关文章

安灯系统为照明工厂打造智能化解决方案

在当今快速发展的制造业领域&#xff0c;智能化转型已成为众多企业提升竞争力的关键。对于照明工厂而言&#xff0c;如何提高生产效率、降低成本、确保产品质量&#xff0c;成为了亟待解决的问题。而安灯系统的出现&#xff0c;为照明工厂提供了一种智能化的解决方案。 一、照明…

GPT-SovitsV2,支持多语种,多音字优化,更好的音色,ZeroShot(WIN/MAC)

语音克隆项目GPT-Sovits发布了V2版本&#xff0c;在早些时候做了V1版本的整合包&#xff0c;但是那个版本的整合包操作比较麻烦&#xff0c;上手难度高。正好趁着V2&#xff0c;一起更新了。 【GPT-SovitsV2,支持多语种,多音字优化,更好的音色,ZeroShot&#xff08;WIN/MAC&…

计算机毕业设计推荐-基于python的个性化旅游路线推荐平台

&#x1f496;&#x1f525;作者主页&#xff1a;毕设木哥 精彩专栏推荐订阅&#xff1a;在 下方专栏&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; 实战项目 文章目录 实战项目 一、基于python的个性化旅游路线…

解决 RT-Thread bsp stm32l476-st-nucleo STM32L4 HAL库缺失问题

问题描述 当前最新的 RT-Thread 版本&#xff1a;5.2.0&#xff0c;发现在 编译 BSP stm32l476-st-nucleo&#xff0c;缺少了 STM32L4xx_HAL 驱动库&#xff0c;造成生成的 工程&#xff0c;如 Keil MDK5 工程无法编译通过 初步的【临时】解决方法是 回退 RT-Thread 的版本&am…

攻防世界 倒车-x64Elf-100

前言&#xff1a;学习笔记。 下载解压 查壳。 64ida 打开。 跟进跳转。 判断&#xff0c;常规就是&#xff0c;左边 1 a1[i]就是flag的值 挺简单的&#xff0c;直接用C语言&#xff0c;照抄就行。 脚本&#xff1a; Code_Talkers

治经济学(Political Economy)是经济学科的总名称

政治经济学&#xff0c;作为一门学科&#xff0c;具有深厚的历史背景和广泛的研究领域。 以下是对政治经济学的详细阐述&#xff1a; 一、定义与起源 政治经济学&#xff08;Political Economy&#xff09;是经济学科的总名称&#xff0c;广义地说&#xff0c;是研究一个社会…

STM32标准库HAL库——MPU6050原理和代码

目录 陀螺仪相关基础知识&#xff1a; 加速度计&#xff0c;陀螺仪的工作原理&#xff1a; 陀螺仪再智能车中的应用&#xff1a; MPU6050原理图和封装图&#xff1a; 硬件IIC和软件IIC的区别&#xff1a; 相同点 不同点 常规获取陀螺仪数据&#xff1a; 标准库&#x…

Vue学习--- vue3 集成遇到的部分问题与解决

构建异常 1. 问题&#xff1a;ESLint: Do not access Object.prototype method hasOwnProperty from target o 报错解释&#xff1a; ESLint 报错信息 "Do not access Object.prototype method hasOwnProperty from target object" 指的是不应该从目标对象访问 Ob…

9个最流行的文本转语音引擎【TTS 2024】

在快速发展的技术世界中&#xff0c;文本转语音 (TTS) 引擎正在取得显著进步。从增强各种应用程序中的用户体验到创建逼真且引起情感共鸣的语音输出&#xff0c;TTS 引擎正变得不可或缺。在这里&#xff0c;我们介绍了 2024 年为行业树立新标准的九款最佳 TTS 引擎。 NSDT工具推…

传统网络编程有什么问题

文章目录 多线程版网络编程客户端MyServerThread服务端 线程池版的网络编程客户端MyServerThread服务端 总结 传统网络通信中的开发方式及问题 多线程版网络编程 下面先写一个多线程版网络编程的版本代码: 客户端 public static void main(String[] args) throws IOExceptio…

【推荐100个unity插件之27】推荐5种办法实现unity人物布料系统 衣服裙子飘动 头发飘动 胸部抖动 骨骼模拟 配件摆动 尾巴摆动

最终效果 文章目录 最终效果前言模型获取一、animation rigging 和 cloth布料模拟二、Unity-Chan!Model三、Dynamic Bone四、Magica Cloth 1五、Magica Cloth 21、介绍2、下载3、官方文档4、安装插件5、使用Animation Rigging插件可视化骨骼6、Magica Cloth介绍7、BoneCloth的使…

【采集软件】根据关键词批量采集小红薯,含笔记正文、笔记链接、发布时间、转评赞藏等

一、背景介绍 1.1 爬取目标 熟悉我的小伙伴都了解&#xff0c;我之前开发过2款软件&#xff1a; 【采集软件】用Python开发的小红薯搜索采集工具&#xff0c;支持多关键词同时&#xff01; 【采集软件】用Python开发的小红薯详情批量采集工具&#xff0c;含笔记正文、转评赞藏…

linux系统使用 docker 来部署运行 mysql8 并配置 docker-compose-mysql.yml 文件

Docker是一个开源的容器化平台&#xff0c;旨在简化应用程序的创建、部署和管理。它基于OS-level虚拟化技术&#xff0c;通过将应用程序和其依赖项打包到一个称为容器的标准化单元中&#xff0c;使得应用程序可以在任何环境中快速、可靠地运行。 Docker的优势有以下几个方面&a…

【网格dp】力扣1594. 矩阵的最大非负积

给你一个大小为 m x n 的矩阵 grid 。最初&#xff0c;你位于左上角 (0, 0) &#xff0c;每一步&#xff0c;你可以在矩阵中 向右 或 向下 移动。 在从左上角 (0, 0) 开始到右下角 (m - 1, n - 1) 结束的所有路径中&#xff0c;找出具有 最大非负积 的路径。路径的积是沿路径访…

Java 入门指南:异常处理的实践规范

在 Java 中处理异常并不是一个简单的事情。需要花费很多时间来思考如何处理异常&#xff0c;包括需要处理哪些异常&#xff0c;怎样处理等等。 抛出或捕获异常的时候&#xff0c;有很多不同的情况需要考虑&#xff0c;而且大部分事情都是为了改善代码的可读性或者 API 的可用性…

捏蛋糕修牛蹄类型的解压视频素材去哪里找?

今天我们聊聊在哪里能找到制作捏蛋糕、修牛蹄等解压视频的素材。这类视频看起来心情就变好&#xff0c;特别解压。如果你也有兴趣制作这种视频&#xff0c;以下是一些优质的素材网站推荐&#xff0c;助你轻松找到所需素材。 蛙学网 开始我们的推荐列表是蛙学网。这是一个综合性…

npm国内源设置

一、背景 在国内使用npm时&#xff0c;由于网络问题&#xff0c;经常会遇到速度慢或无法访问的问题。为了提高效率&#xff0c;可以将npm的源设置为国内的镜像源。以下是一些常用的国内npm镜像源以及如何设置它们的方法。 二、国内可用源 2.1 淘宝npm源 https://registry.np…

SOLIDWORKS 2025全新功能解读:界面优化

准备好在SOLIDWORKS 2025中探索了吗?新版本&#xff0c;可帮助您简化和加速从概念到制造的产品开发流程&#xff0c;鑫辰科技带您抢先体验SOLIDWORKS 2025的亮点&#xff0c;深入了解新版本所增添的独特功能。 一&#xff1a;指定 Z-向上模板 在早期版本中&#xff0c;SOLID…

手算神经网络MAC和FLOP

在本文中&#xff0c;我们将深入探讨神经网络背景下的 MAC&#xff08;乘法累加运算&#xff09;和 FLOP&#xff08;浮点运算&#xff09;概念。通过学习如何使用笔和纸手动计算这些内容&#xff0c;你将对各种网络结构的计算复杂性和效率有基本的了解。 这是 colab 笔记本中…

使用 Python 和 SQL 自动将 ETL 传输到 SFTP 服务器

了解如何在 Windows 上自动执行从 PostgreSQL 数据库到远程服务器的日常数据传输过程 欢迎来到雲闪世界。将文件从一个位置传输到另一个位置的过程显然是自动化的完美选择。重复执行这项工作可能令人望而生畏&#xff0c;尤其是当您必须对几组数据执行整个 ETL&#xff08;提取…