SpringBoot+dynamic-datasource实现多数据源(msyql、sqlserver、postgresql)手动切换

场景

SpringBoot+MybatisPlus+dynamic-datasources实现连接Postgresql和mysql多数据源:

SpringBoot+MybatisPlus+dynamic-datasources实现连接Postgresql和mysql多数据源-CSDN博客

上面实现通过注解和配置文件的方式去进行多数据源操作。

如果业务需求,比如查询第三方接口时提供的是sqlserver的视图连接方式时,需要在调用

接口时手动新增数据源-检验数据源是否可用-切换当前数据源-查询数据-清除当前数据源

实现以上流程,可以通过mybatisplus的dynamic-datasource来实现。

dynamic-datasource

开源文档付费

基础必读(免费) · dynamic-datasource · 看云

多数据源 | MyBatis-Plus

但是可以通过引入依赖后查看其源码以及借助网上一些教程,可以找到源码中需要用到的几个类

注:

博客:
霸道流氓气质-CSDN博客

实现

1、引入pom依赖

这里springboot的版本是2.6.13

dynamic-datasource的版本是3.2.1

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

引入msyql、sqlserver、postgresql所需的依赖

        <!--MySQL驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
        <!-- sqlserver-->
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <version>7.4.1.jre8</version>
        </dependency>

引入Mybatisplus所需依赖

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>

引入其他依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>   
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

2、这里使用默认的HikariCP数据库连接池,没使用Druid,流程一样

参考上面博客已经实现了连接Mysql和sqlserver多数据源

spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
         url:jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
          dbcp2:
            min-idle: 5                                # 数据库连接池的最小维持连接数
            initial-size: 5                            # 初始化连接数
            max-total: 5                               # 最大连接数
            max-wait-millis: 150                       # 等待连接获取的最大超时时间

        pg:
          url: jdbc:postgresql://127.0.0.1:5432/test
          username: postgres
          password: 123456
          driver-class-name: org.postgresql.Driver
          dbcp2:
            min-idle: 5                                # 数据库连接池的最小维持连接数
            initial-size: 5                            # 初始化连接数
            max-total: 5                               # 最大连接数
            max-wait-millis: 150                       # 等待连接获取的最大超时时间

3、SpringBoot+dynamic-datasource使用DynamicRoutingDataSource获取当前所有数据源

代码实现:

@SpringBootTest
class DynamicDataSourceTest {

    @Autowired
    private DataSource dataSource;


    /**
     * 获取当前所有数据源
     */
    @Test
    void getAllDataSource() {
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        System.out.println(ds.getCurrentDataSources().keySet());
    }
}

其中DataSource是javax.sql包下的。

javax.sql.DataSource 是 jdk 提供的接口,各个连接池厂商 和 Spring 都对 DataSource 进行了设计和实现。

javax.sql.DataSource 是连接到物理数据源的工厂接口。它是 java.sql.DriverManager 功能的替代者,

是获取数据库连接的首选方法。

DataSource 数据源在必要时可以修改它的属性。例如,如果将数据源移动到其他服务器,

则可以更改 DataSource 的属性,这样访问该数据源的代码不需要做任何更改就可以获取到达到目的。

单元测试运行结果:

4、SpringBoot+DynamicRoutingDataSources实现添加与删除数据源

这里通过代码将sqlserver的数据源添加到DynamicRoutingDataSource中

这就需要用到creator包下的各种创建器

这里是默认HikariCP的连接池,所以使用该创建器,如果是其它类型则使用对应的创建器。

数据源创建需要的参数这里新建一个DTO类用来赋值

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class DataSourceDTO {
    private String dataSourceName;
    private String driverClassName;
    private String url;
    private String username;
    private String password;
}

编写单元测试

@SpringBootTest
class DynamicDataSourceTest {

    @Autowired
    private DataSource dataSource;

    @Autowired(required = false)
    private HikariDataSourceCreator hikariDataSourceCreator;

    /**
     * 获取当前所有数据源
     */
    @Test
    void getAllDataSource() {
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        System.out.println(ds.getCurrentDataSources().keySet());
    }

    /**
     * 添加 与 删除 HikariCP数据源
     */
    @Test
    void addHcpDataSource() {
        DataSourceDTO sqlserver = DataSourceDTO.builder()
                .dataSourceName("sqlserver")
                .driverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver")
                .url("jdbc:sqlserver://127.0.0.1:1433;DatabaseName=test")
                .username("sa")
                .password("123456")
                .build();
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        BeanUtils.copyProperties(sqlserver,dataSourceProperty);
        DataSource sqlserverDataSource = hikariDataSourceCreator.createDataSource(dataSourceProperty);
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        ds.addDataSource(sqlserver.getDataSourceName(),sqlserverDataSource);
        System.out.println(ds.getCurrentDataSources().keySet());
        ds.removeDataSource(sqlserver.getDataSourceName());
        System.out.println(ds.getCurrentDataSources().keySet());
    }
}

单元测试运行结果:

5、SpringBoot中+HikariCP实现检测数据源是否可用

预实现手动切换当前数据源,首先需要确保配置的数据源的相关参数可以正常连接可用

新建检测数据源的方法

    /**
     *  检测数据源
     * @param dataSourceDTO
     * @return
     */

    private boolean checkDataBase(DataSourceDTO dataSourceDTO){
        Connection connection = null;
        try {
            DataSource dataSource = creatHcpDataSource(dataSourceDTO);
            connection = dataSource.getConnection();
            Statement statement = connection.createStatement();
            statement.execute("select * from s_user");
            return true;
        } catch (Exception exception) {
            return false;
        }finally {
            try {
                if(null!=connection){
                    connection.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }

这其中就是创建数据源并进行连接和执行sql,确保无异常发生则代表连接可用,这里执行的sql以及表名

均写死,其中s_user是sqlserver中新建的表。

用到创建数据源的方法creatHcpDataSource的实现

    /**
     * 创建Hcp datasource
     * @param dataSourceDTO
     * @return
     */
    private DataSource creatHcpDataSource(DataSourceDTO dataSourceDTO){
        HikariDataSource dataSource = new HikariDataSource();
        //控制客户端(即用户程序)等待池中连接的最长毫秒数。如果在没有连接可用的情况下超过此时间,则将抛出SQLException异常。最低可以接受的连接超时为250毫秒。默认值:3000(30秒)。这是一个很重要的问题排查指标
        dataSource.setConnectionTimeout(3000l);
        //HikariCP将尝试仅基于jdbcUrl通过DriverManager解析驱动程序,但对于某些较旧的驱动程序必须指定driverClassName。除非用户收到明显的错误消息,表明未找到驱动程序,否则可忽略此属性。默认值:无
        //dataSource.setDataSourceClassName(dataSourceDTO.getDriverClassName());
        dataSource.setJdbcUrl(dataSourceDTO.getUrl());
        dataSource.setUsername(dataSourceDTO.getUsername());
        dataSource.setPassword(dataSourceDTO.getPassword());
        //表示连接池的用户定义名称,主要显示在日志记录和JMX管理控制台中,以标识池和池配置。该属性的默认值:自动生成
        dataSource.setPoolName(dataSourceDTO.getDataSourceName());
        return dataSource;
    }

相关参数和配置自行搜索和设置,这里重点关注

dataSource.setConnectionTimeout(3000l);

设置超时时间3秒。

控制客户端(即用户程序)等待池中连接的最长毫秒数。如果在没有连接可用的情况下超过此时间,则将抛出SQLException异常。

最低可以接受的连接超时为250毫秒。默认值:3000(30秒)。这是一个很重要的问题排查指标

dataSource.setPoolName

示连接池的用户定义名称,主要显示在日志记录和JMX管理控制台中,以标识池和池配置。该属性的默认值:自动生成

编写单元测试:

    /**
     * 检测数据源
     */
    @Test
    void testDataSource(){
        DataSourceDTO sqlserver = DataSourceDTO.builder()
                .dataSourceName("sqlserver")
                .driverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver")
                .url("jdbc:sqlserver://127.0.0.1:1433;DatabaseName=test")
                .username("sa")
                .password("123456")
                .build();
        boolean checkDataBase = checkDataBase(sqlserver);
        System.out.println(checkDataBase);
        DataSourceDTO sqlserverWrong = DataSourceDTO.builder()
                .dataSourceName("sqlserver")
                .driverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver")
                .url("jdbc:sqlserver://192.168.1.2:1433;DatabaseName=test")
                .username("sa")
                .password("123456")
                .build();
        boolean checkDataBaseWrong = checkDataBase(sqlserverWrong);
        System.out.println(checkDataBaseWrong);
    }

第二个故意写了一个不存在的数据源的地址

运行单元测试:

6、SpringBoot+DynamicDataSourceContextHolder实现手动切换清除当前数据源

动态数据源切换的关键在于DynamicDataSourceContextHolder类,它提供了一种机制来存储当前使用的数据源。

它主要由两部分组成,一部分是线程本地的数据源容器,另一部分是管理动态数据源的数据源切换类。

查看其源码

主要用到的方法

push 设置当前线程数据源 如非必要不要手动调用,调用后确保最终清除

poll 清空当前线程数据源 如果当前线程是连续切换数据源 只会移除掉当前线程的数据源名称

clear 强制清空本地线程  防止内存泄漏,如手动调用了push可调用此方法确保清除

编写单元测试:

    /**
     * 手动切换数据源
     */
    @Test
    void changeDataSource() {
        DataSourceDTO sqlserver = DataSourceDTO.builder()
                .dataSourceName("sqlserver")
                .driverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver")
                .url("jdbc:sqlserver://127.0.0.1:1433;DatabaseName=test")
                .username("sa")
                .password("123456")
                .build();
        boolean checkDataBase = checkDataBase(sqlserver);
        if(checkDataBase){
            DataSourceProperty dataSourceProperty = new DataSourceProperty();
            BeanUtils.copyProperties(sqlserver,dataSourceProperty);
            DataSource sqlserverDataSource = hikariDataSourceCreator.createDataSource(dataSourceProperty);
            DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
            Set<String> dataSourceSet = ds.getCurrentDataSources().keySet();
            if (!dataSourceSet.contains(sqlserver.getDataSourceName())) {
                ds.addDataSource(sqlserver.getDataSourceName(),sqlserverDataSource);
            }
            //push 设置当前线程数据源 如非必要不要手动调用,调用后确保最终清除
            DynamicDataSourceContextHolder.push(sqlserver.getDataSourceName());
            List<SUser> sUsers = sUserMapper.selectList(new LambdaQueryWrapper<>());
            System.out.println(sUsers);
            //DynamicDataSourceContextHolder.poll();
            //poll 清空当前线程数据源 如果当前线程是连续切换数据源 只会移除掉当前线程的数据源名称
            //clear 强制清空本地线程  防止内存泄漏,如手动调用了push可调用此方法确保清除
            DynamicDataSourceContextHolder.clear();
            ds.removeDataSource(sqlserver.getDataSourceName());
        }else {
            System.out.println("数据源连接异常");
        }
    }

这里一定要对数据源可用性进行校验,确保可用才进行数据源添加和切换当前数据源操作

其中SUser是Sqlserver中新建表对应的实体类

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "s_user")
public class SUser implements Serializable {

    private static final long serialVersionUID = -5514139686858156155L;

    private Integer id;

    private String name;

    private Integer age;

    private String address;

}

调用查询的mapper接口实现

import com.badao.demo.entity.SUser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;

@Repository
public interface SUserMapper extends BaseMapper<SUser> {

}

单元测试运行结果

7、将以上手动切换数据源和执行业务逻辑的操作封装成工具类

import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import java.util.function.Supplier;

/**
 * 数据源切换工具类
 */
public class DynamicDSExecute {

    public static <T> T execute(String dsName, Supplier<T> executor){
        try {
            DynamicDataSourceContextHolder.push(dsName);
            return executor.get();
        }finally {
            DynamicDataSourceContextHolder.clear();
        }
    }
}

这里用到了函数式接口,可参考如下

java8中常用函数式接口Supplier<T>、Consumer<T>、Function<T,R>、Predicate<T>使用示例:

java8中常用函数式接口Supplier<T>、Consumer<T>、Function<T,R>、Predicate<T>使用示例_java8 函数式编程supplier接口案例大全-CSDN博客

然后调用工具类时

            Supplier<List<SUser>> supplier = () -> sUserMapper.selectList(new LambdaQueryWrapper<>());
            List<SUser> sUsers = DynamicDSExecute.execute(sqlserver.getDataSourceName(),supplier);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu安装最新版Docker和Docker-Compose

ubuntu环境搭建专栏&#x1f517;点击跳转 Ubuntu系统环境搭建&#xff08;十&#xff09;——Ubuntu安装最新版Docker和Docker Compose 文章目录 Ubuntu系统环境搭建&#xff08;十&#xff09;——Ubuntu安装最新版Docker和Docker Compose1.添加Docker库1.1 安装必要的证书并…

PostgreSQL命令大全

文章目录 连接与退出数据库操作表操作外键约束视图操作存储过程与函数权限管理事务管理查询优化与分析数据类型转换分区表操作复制与备份恢复 PostgreSQL是一个功能强大的开源关系型数据库管理系统&#xff0c;以下是一些基本且常用的命令按功能分类&#xff1a; 连接与退出 连…

【前端设计】流光按钮

欢迎来到前端设计专栏&#xff0c;本专栏收藏了一些好看且实用的前端作品&#xff0c;使用简单的html、css语法打造创意有趣的作品&#xff0c;为网站加入更多高级创意的元素。 css body{height: 100vh;display: flex;justify-content: center;align-items: center;background…

HarmonyOS鸿蒙学习基础篇 - 项目目录和文件介绍

├── hvigor //存储购置信息的文件&#xff0c;主要用于发布打包 ├── idea //开发工具相关配置可忽略 ├── AppScope //工程目录 全局公共资源存放路径 │ └── resources │ │ └── base │ │ │ └── element //常亮存放 │ │ │ …

Windows安装WSL2精简版教程

文章目录 一、安装WSL二、更改WSL的存放路径/备份WSL三、安装WSL Terminall四、WSL界面&#xff1a;xlaunch五、WSL1升级WSL2六、WSL2与VMware兼容问题七、更改手动导入的wsl的默认登录用户参考 一、安装WSL 步骤1 - 启用适用于 Linux 的 Windows 子系统&#xff1a; 需要先启…

格局打开!前端未死,只是要求变高了

本文笔者会从以下几个方面分享&#xff0c;希望能够帮助正在迷茫的前端小伙伴提供一点思路&#xff01; 逛技术博客 不局限框架 全栈工程师兴起 关注前沿 写技术文章 录制前端视频 总结 2024年了&#xff0c;没工作的找到工作了吗&#xff1f;有工作的加薪了吗&#xff1f;加薪…

UI设计中的插画运用优势(上)

1. 插画是设计的原创性和艺术性的基础 无论是印刷品、品牌设计还是UI界面&#xff0c;更加风格化的插画能够将不同的风格和创意加入其中&#xff0c;在激烈的竞争中更容易因此脱颖而出。留下用户才有转化。 2. 插画是视觉触发器&#xff0c;瞬间传达大量信息 我们常说「一图胜千…

Unity3D学习之Unity基础

文章目录 1. 第一部分&#xff1a;MONO中的重要内容2. 延时函数2.1 什么是延时函数2.2 延时函数的使用2.2.1 延时重复函数2.2.2 取消延迟函数2.2.3 判断是否有延迟函数开启2.2.4 延迟函数和依附对象的关系 3 协同程序3.1 Unity中的多线程3.2 协同程序3.3 协程的使用3.3.1 关闭协…

Jetson Orin Nano安装OpenCV带cuda加速版本的全过程

安装过程 使用jetpack安装的jetson&#xff0c;自带了opencv&#xff0c;但是没有cuda加速的&#xff0c;输入opencv_version 使用jtop查看&#xff0c;可以确认自带的opencv是没用cuda的 卸载opencv&#xff0c;先查看有哪些包 pip3 list | grep opencv opencv-python 然后卸…

从规则到神经网络:机器翻译技术的演化之路

文章目录 从规则到神经网络&#xff1a;机器翻译技术的演化之路一、概述1. 机器翻译的历史与发展2. 神经机器翻译的兴起3. 技术对现代社会的影响 二、机器翻译的核心技术1. 规则基础的机器翻译&#xff08;Rule-Based Machine Translation, RBMT&#xff09;2. 统计机器翻译&am…

文献分享四:(基础)Pyroelectric drift of integrated-optical LiNbO3 modulators

文献来源——S. M. Kostritskii, Yu. N. Korkishko, V. A. Fedorov & A. V. Yatsenko (2021) Pyroelectric drift of integrated-optical LiNbO3 modulators, Ferroelectrics, 574:1, 170-178, DOI: 10.1080/00150193.2021.1888062 本文分析了我们之所以需要偏置电路的原因…

力扣刷MySQL-第六弹(详细讲解)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;力扣刷题讲解-MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出…

【Linux、C】服务程序的调度

这一节中用到的技术点有&#xff1a;信号、多进程、exec函数 exec函数族 exec的本质 exec是用参数中指定的程序替换了当前进程的正文段、数据段、堆和栈。 如果调用失败会继续运行当前进程 #include <stdio.h> #include <stdlib.h> #include <string.h> #in…

常用排序算法总结(直接插入排序、选择排序、冒泡排序、堆排序、快速排序、希尔排序、归并排序)

目录 一. 直接插入排序 二:选择排序 三:冒泡排序 四.堆排序 五:希尔排序 六:快速排序(递归与非递归) 七.归并排序(递归与非递归) 一. 直接插入排序 &#x1f31f;排序思路 直接插入排序的基本原理是将一条记录插入到已排好的有序表中&#xff0c;从而得到一个新的、记录…

PaddleNLP评论观点抽取和属性级情感分析

项目地址&#xff1a;PaddleNLP评论观点抽取和属性级情感分析 - 飞桨AI Studio星河社区 (baidu.com) 情感分析旨在对带有情感色彩的主观性文本进行分析、处理、归纳和推理&#xff0c;其广泛应用于消费决策、舆情分析、个性化推荐等领域&#xff0c;具有很高的商业价值。 依托…

mysql综合实验

USE dept_emp; CREATE TABLE dept (deptno INT(2) NOT NULL COMMENT 部门编号,dname VARCHAR (15) COMMENT 部门名称,loc VARCHAR (20) COMMENT 地理位置 );-- 添加主键 ALTER TABLE dept ADD PRIMARY KEY (deptno); INSERT INTO dept (deptno,dname,loc)VALUES (10,财务…

Windows 11 UEFI引导修复的方法有哪些?

若Windows 11 UEFI 引导加载程序损坏了&#xff0c;您的电脑将无法启动&#xff0c;那么Win11怎么修复UEFI引导&#xff1f;下面我们就来了解一下。 通过自动修复进行UEFI引导修复 1. 将可启动U盘连接到损坏的电脑&#xff0c;进入BIOS设置您的电脑从U盘启动电脑。然后&#x…

TypeScript语法总结

JavaScript 与 TypeScript 的区别 TypeScript 是 JavaScript 的超集&#xff0c;扩展了 JavaScript 的语法&#xff0c;因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改&#xff0c;TypeScript 通过类型注解提供编译时的静态类型检查。 TypeScript 可处理已…

【论文解读】LERF:语言嵌入的辐射场(ICCV 2023 Oral)

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 论文链接&#xff1a;https://arxiv.org/abs/2303.09553 项目主页&#xff1a;https://lerf.io](https://lerf.io 图 1&#xff1a;语言嵌入辐射场 (LERF)。 LERF 将 CLIP 表示建立在密集、多尺度的 3D 场中。…

Unity导出Android项目踩坑记录

导出的时候需要注意以下地方的配置&#xff1a; 1、buildSetting-> 设置ExportProject 2、buildsetting ->playerSetting ->设置IL2CPP 3、设置ndk edit->preferences->external tools->ndk 如果unity的ndk版本和android项目里的ndk版本不一致会报错&…