实例讲解Spring boot动态切换数据源

2023-11-11

前言

在公司的系统里,由于数据量较大,所以配置了多个数据源,它会根据用户所在的地区去查询那一个数据库,这样就产生了动态切换数据源的场景。

今天,就模拟一下在主库查询订单信息查询不到的时候,切换数据源去历史库里面查询。

实现效果

首先我们设置查询的数据库为db1,可以看到通过订单号没有查到订单信息,然后我们重置数据源,重新设置为db2,同样的订单号就可以查询到信息。

数据库准备

新建两个数据库db1和db2,db1作为主库,db2作为历史库

两个库中都有一个订单表biz_order,主库中没有数据,历史库中有我们要查询的数据。

代码编写

1.新建一个springboot项目,引入所需依赖

复制代码

 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter</artifactId>
 </dependency>
 <!--引入druid-替换默认数据库连接池-->
 <dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>druid-spring-boot-starter</artifactId>
 <version>1.2.15</version>
 </dependency>
 <dependency>
 <groupId>org.mybatis.spring.boot</groupId>
 <artifactId>mybatis-spring-boot-starter</artifactId>
 <version>2.2.2</version>
 </dependency>
 <!--mysql驱动-->
 <dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <version>8.0.30</version>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 <scope>test</scope>
 </dependency>

复制代码

2.application.yaml配置数据库信息

这里我们配置两个数据库的信息

复制代码

spring:
 datasource:
    db1:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost/db1?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=false
      username: root
      password: root
      type: com.alibaba.druid.pool.DruidDataSource
    db2:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost/db2?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=false
      username: root
      password: root
      type: com.alibaba.druid.pool.DruidDataSource
mybatis:
  mapper-locations: classpath:mapper/*.xml

复制代码

3.创建数据源对象,并注入spring容器中

新建DynamicDataSourceConfig.java文件,在该配置文件中读取yaml配置的数据源信息,并且通过该信息构造数据源对象,然后通过@Bean注解注入到spring容器中。

复制代码

package com.it1997.config;
import com.alibaba.druid.pool.DruidDataSource;
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.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
@Configuration
public class DynamicDataSourceConfig {
    @Bean("dataSource1")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
 public DataSource oneDruidDataSource() {
 return DruidDataSourceBuilder.create().build();
 }
    @Bean("dataSource2")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
 public DataSource twoDruidDataSource() {
 return DruidDataSourceBuilder.create().build();
 }
    @Bean
 public DataSourceTransactionManager dataSourceTransactionManager1(@Qualifier("dataSource1") DataSource dataSource1) {
 DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
 dataSourceTransactionManager.setDataSource(dataSource1);
 return dataSourceTransactionManager;
 }
    @Bean
 public DataSourceTransactionManager dataSourceTransactionManager2(@Qualifier("dataSource2") DataSource dataSource2) {
 DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
 dataSourceTransactionManager.setDataSource(dataSource2);
 return dataSourceTransactionManager;
 }
}

复制代码

4.数据源配置上下文信息

新建DynamicDataSourceHolder.java文件,该文件通过ThreadLocal,实现为每一个线程创建一个保存数据源配置的上下文。并且提供setDataSource和getDataSource静态方法来设置和获取数据源的名称。

复制代码

package com.it1997.config;
public class DynamicDataSourceHolder {
 private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
 public static void setDataSource(String dataSource) {
 contextHolder.set(dataSource);
 }
 public static String getDataSource() {
 return contextHolder.get();
 }
 public static void clearDataSource() {
 contextHolder.remove();
 }
}

复制代码

5.重写数据源配置类

新建DynamicDataSource.java文件,该类继承AbstractRoutingDataSource 类,重写父类determineCurrentLookupKey和afterPropertiesSet方法。

这里我们重写父类中afterPropertiesSet方法(为什么要重写在这个方法,可以看文章最后对于druid的源码的讲解),在这个方法里我们将spring容器中的所有的数据源,都给放到map里,然后后续我们根据map中的key来获取不同的数据源,super.afterPropertiesSet();通过这个方法设置上数据源。

在类上加上@Primary注解,让spring容器优先使用我们自定义的数据源,否则还是会使用默认的数据源配置。

复制代码

package com.it1997.config;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Component
@Primary
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Resource
 DataSource dataSource1;
    @Resource
 DataSource dataSource2;
    @Override
 protected Object determineCurrentLookupKey() {
 return DynamicDataSourceHolder.getDataSource();
 }
    @Override
 public void afterPropertiesSet() {
 // 初始化所有数据源
        Map<Object, Object> targetDataSource = new HashMap<>();
 targetDataSource.put("db1", dataSource1);
 targetDataSource.put("db2", dataSource2);
 super.setTargetDataSources(targetDataSource);
 super.setDefaultTargetDataSource(dataSource1);
 super.afterPropertiesSet();
 }
}

复制代码

druid数据源配置解读

点开我们刚刚继承的AbstractRoutingDataSource抽象类,可以看到它又继承了AbstractDataSource 实现了InitializingBean接口。

然后我们在看一下druid的数据源配置是怎么实现的,点开DruidDataSourceWrapper类,可以看到它也是继承了AbstractDataSource 实现了InitializingBean接口。并且,读取的是yaml文件中spring.datasource.druid下面配置的数据库连接信息。

而我们自定的一的数据源读取的是spring.datasource.db1下面配置的数据库连接信息。

druid的数据源配置,实现了接口中afterPropertiesSet,在这个方法中设置了数据库的基本信息,例如,数据库连接地址、用户名、密码以及数据库连接驱动信息。 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

实例讲解Spring boot动态切换数据源 的相关文章

  • 单击输入[复选框]的标签将触发父级单击事件两次(淘汰)

    考虑这把小提琴 http jsfiddle net 9rkrahm6 我有一个
  • 如何在同一页面上使用AJAX处理多个表单

    我有一个表单 当我单击 提交 时 它就被提交了 然后该表单隐藏 操作页面的结果显示在 div 中 classname dig 它工作正常 但是当我添加另一个表单时 它停止正常工作并且所有表单同时提交 我如何更改我的代码 done click
  • 使用 useReducers 调度函数发送多个操作?

    使用时是否可以通过调度函数发送多个动作useReducer挂钩反应 我尝试向它传递一组操作 但这会引发未处理的运行时异常 明确地说 通常会有一个初始状态对象和一个减速器 如下所示 const initialState message1 nu
  • jQuery AJAX 调用 Java 方法

    使用 jQuery AJAX 我们可以调用特定的 JAVA 方法 例如从 Action 类 该 Java 方法返回的数据将用于填充一些 HTML 代码 请告诉我是否可以使用 jQuery 轻松完成此操作 就像在 DWR 中一样 此外 对于
  • 在 Wordpress 站点中进行 AJAX 调用时出现问题

    我在使用 Wordpress 站点功能的 AJAX 部分时遇到了一些问题 该功能接受在表单上输入的邮政编码 使用 PHP 函数来查找邮政编码是否引用特定位置并返回到该位置的永久链接 我的第一个问题是关于我构建的表单 现在我的表单操作是空白的
  • Google App Engine:修改云运行环境

    我正在尝试部署一个使用自定义 Node js 服务器的 Next js 应用程序 我想将自定义构建变量注入应用程序 next config js const NODE ENV process env NODE ENV const envTy
  • 除了更改标题之外,如何在 Firefox 中强制另存为对话框?

    有没有办法在 ff 中强制打开 www example com example pdf 的另存为对话框 我无法更改标题 如果您可以将文件以 Base64 格式输出到客户端 则可以使用 data uri 进行下载 location href
  • 标签获取 href 值

    我有以下 html div class threeimages a img alt Australia src Images Services 20button tcm7 9688 gif a div class text h2 a hre
  • 将div设置为隐藏,延时后可见

    我试图在 X 时间后 也许甚至在随机时间之后 但现在我们只做固定时间 在黑色背景上出现一个黄色方块 function initialSetup if document getElementById yellow null document
  • Grails 在 javascript 内的 GSP 站点中使用 grails var

    我有一个在 GSP 文件中的 javascript 代码中使用 grails 变量值的问题 例如 我有一个会话值session getAttribute selectedValue 我想在 javascript 代码部分使用这个值 我现在的
  • Javascript 数组到 VBScript

    我有一个使用 Javascript 构建的对象数组 我需要使用 VBScript 读取它 如下例所示 我找不到在 VbScript 代码中循环遍历数组的方法myArray object 这个例子是我的问题的简化 我无法更改页面的默认语言 这
  • HTML 离线应用程序缓存,列出下载的文件

    作为我正在构建的离线 Web 应用程序的加载屏幕的一部分 使用缓存清单 http developer apple com library safari documentation iPhone Conceptual SafariJSData
  • 如何获取给定 DOM 元素的所有定义的 CSS 选择器?

    如何使用 jQuery 获取给定 DOM 元素的所有定义的 CSS 选择器 定义后 我的意思是在应用于任何样式表的所有 CSS 选择器document 在某种程度上 这类似于 FireBug 实现的功能 其中显示所选 DOM 元素的所有应用
  • 条件在反应本机生产中失败,但在开发中有效

    我创建了一个反应本机应用程序 我需要通过它进行比较 如果属实 就会执行死刑 问题是 该条件适用于 React Native 开发模式 而不适用于 React Native 生产版本 我使用 firebase 作为数据库 也使用 redux
  • Safari 支持 JavaScript window.onerror 吗?

    我有一个附加到 window onerror 的函数 window onerror function errorMsg url line window alert asdf 这在 firefox chrome 和 IE 中工作正常 但在 s
  • 如何更改此 jquery 插件的时区/时间戳?

    我正在使用这个名为 timeago 的插件 在这里找到 timeago yarp com 它工作得很好 只是它在似乎不同的时区运行 我住在美国东部 费城时区 当我将准确的 EST 时间放入 timeago 插件时 比如 2011 05 28
  • JQuery 图像上传不适用于未来的活动

    我希望我的用户可以通过帖子上传图像 因此 每个回复表单都有一个上传表单 用户可以通过单击上传按钮上传图像 然后单击提交来提交帖子 现在我的上传表单可以上传第一个回复的图像 但第二个回复的上传不起作用 我的提交过程 Ajax 在 php 提交
  • 如何获取浏览器视口中当前显示的内容

    如何获取当前正在显示长文档的哪一部分的指示 例如 如果我的 html 包含 1 000 行 1 2 3 9991000 并且用户位于显示第 500 行的中间附近 那么我想得到 500 n501 n502 或类似的内容 显然 大多数场景都会比
  • 使用 Ajax 请求作为源数据的 Jquery 自动完成搜索

    我想做的事 我想使用 jquery 自动完成函数创建一个输入文本字段 该函数从跨域curl 请求获取源数据 结果应该与此示例完全相同 CSS 在这里并不重要 http abload de img jquerydblf5 png http a
  • 导致回发到与弹出窗口不同的页面

    我有一个主页和一个详细信息页面 详细信息页面是从主页调用的 JavaScript 弹出窗口 当单击详细信息页面上的 保存 按钮时 我希望主页 刷新 是否有一种方法可以调用主页的回发 同时还可以从详细信息页面维护保存回发 Edit 使用win

随机推荐

  • Coursera 吴恩达DeepLearning.AI 第五课 sequence model 序列模型 第二周 Operations on word vectors - v2

    只做了计分部分 Operations on word vectors Welcome to your first assignment of this week Because word embeddings are very comput
  • Windows设置redis开启自动启动

    一 首先下载好你需要的Redis 这里就不多说了 可以百度的到的 二 安装完之后 运行redis是通过命令或者是直接运行redisServer的可执行文件 运行效果如下 三 设置Redis服务 由于上面虽然启动了redis 但是只要一关闭c
  • C++11之for循环的新用法

    C 使用如下方法遍历一个容器 include stdafx h include
  • Redis 数据结构与命令

    一 数据结构 Redis是典型的键值数据库 key一般是字符串 而value包含很多不同的数据类型 Value结构 Key结构 Redis的key允许有多个单词形成层级结构 多个单词之间用 隔开 格式为 项目名 业务名 类型 id 如果Va
  • 平滑滤波第二节,中值滤波法(数字图像处理大题复习 P4)

    文章目录 中值滤波 1 圈出模板像素 2 列出像素找到中值 3 填入结果 如果 不要滤波后就替换 中值滤波 1 圈出模板像素 我们先观察这个模板 这里圈出来了5个像素 那我们也在图像中对圈出5个像素 和均值滤波一样 第一个点的选取 2 列出
  • python用input输入list_python怎么用input函数输入一个列表

    python怎么用input函数输入一个列表 函数 的是 数字 没有任何 赋值 python怎么用input函数输入一个列表 易采站长站 站长之家为您整理了python怎么用input函数输入一个列表的相关内容 在Python3 0以后 键
  • JS中设置backgroun-position不成功的问题

    今天在做个人主页的时候 准备把background position设置一下 可是用了很多种方法都没有能够成功 最后才发现了问题 在这里记录一下 如果可以 也很开心能够和你分享我的经验 原生javascript 在原生的js中 大家都理解的
  • vscode不能预览plantuml文件报错的解决方法

    vscode不能预览plantuml文件报错的解决方法 vscode在预览plantuml文件时报错 安装了jre 8u231 dotnet sdk graphviz node终于解决 报错信息如下 扩展宿主意外终止或者 Error wri
  • 数据可视化——Davinci

    数据可视化 Davinci对比及功能使用 一 数据可视化 个人引用部分 1 有趣的意义 2 常规的意义 二 图形的内涵 1 图形类型 2 优秀可视化的特点 三 可视化需求分析 1 四个问题 2 三个沟通点 四 Davinci功能详解 1 可
  • 六度空间理论

    六度空间理论 什么是六度空间理论 Six Degrees of Separation 它是数学领域的一个猜想 名为Six Degrees of Separation 中文翻译包括以下几种 六度空间理论 六度分隔理论 六度分割理论或叫作小世界
  • 论文理解记录:Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration

    论文核心 论文剪枝对象是卷积核 与其他论文不同点在于作者思考了 norm wise 作为卷积核重要性判断的弊端 并提出了 FPGM 算法 该算法先计算出所有卷积核的几何中心 作者把卷积核当作多维空间中的点 然后找到距离几何中心近的卷积核 并
  • robot framework 使用五:CentOS上运行robot framework 并自动发送测试结果

    操作系统版本 centos 6 0 x86 64 想要在linux上运行robot framework的测试用例 需要安装以下工具和软件 1 安装python 2 7 6 首先python version 查看系统是否装有python 并且
  • jQuery操作编辑页面,span与input标签之间的随时转换

    1 input select textarea转span var switchToSpan function console log this attr id var cId this attr id 获取当前点击input的id cons
  • IP:127.0.0.1详解,与localhost区别

    127 0 0 1 127 0 0 1是回送地址 指本地机 一般用来测试使用 回送地址 127 x x x 是本机回送地址 Loopback Address 即主机IP堆栈内部的IP地址 主要用于网络软件测试以及本地机进程间通信 无论什么程
  • AE表达式:利用wiggle和Math.sin制作蝴蝶动画

    AE表达式 利用wiggle和Math sin制作蝴蝶动画 B站链接 AE表达式 B站讲解最详细的Math表达式和蝴蝶动画制作 课程准备 蝴蝶AI素材 wiggle的用法 上次录了一节视频 链接 Math sin三角函数 函数原型 y A
  • php获取脚本执行的参数

    在看PHP文档到预定义变量时碰到了 argc和 argv 顺手记录下 getopt 从命令行参数列表中获取选项 arg getopt d n 只接收d n之后的参数 num arg n arg n 365 是得到的n后面的参数 这种属于短参
  • 流处理系统Heron——architecture

    简介 Heron是Twitter开源的分布式流处理系统 用来在Twitter内部替代Storm 它提供了和Storm兼容的API 并弥补了Storm中的不足 Storm的不足和新的需求 调试困难 在Storm中 一个topology的多个c
  • idea在maven中导入jstl无效解决办法

    第一种方法 首先在pom中添加依赖
  • Scrapy(二)翻页功能

    目录 方法一 利用框架内置函数 方法二 拼接URL 方法三 拼接简化版 Scrapy提供了很多种翻页的方式 记住其中常用的三种即可 下期会将一些CSS基本语法 这是spider pyl里设置即可 方法一 利用框架内置函数 好处 可以从自己设
  • 实例讲解Spring boot动态切换数据源

    前言 在公司的系统里 由于数据量较大 所以配置了多个数据源 它会根据用户所在的地区去查询那一个数据库 这样就产生了动态切换数据源的场景 今天 就模拟一下在主库查询订单信息查询不到的时候 切换数据源去历史库里面查询 实现效果 首先我们设置查询