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

前言

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

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

实现效果

首先我们设置查询的数据库为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.Driverurl: jdbc:mysql://localhost/db1?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=falseusername: rootpassword: roottype: com.alibaba.druid.pool.DruidDataSourcedb2:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost/db2?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=falseusername: rootpassword: roottype: 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();}@Beanpublic DataSourceTransactionManager dataSourceTransactionManager1(@Qualifier("dataSource1") DataSource dataSource1) {DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource1);return dataSourceTransactionManager;}@Beanpublic 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 {@ResourceDataSource dataSource1;@ResourceDataSource dataSource2;@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceHolder.getDataSource();}@Overridepublic 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,在这个方法中设置了数据库的基本信息,例如,数据库连接地址、用户名、密码以及数据库连接驱动信息。 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

elementui 菜单选中优化

/** 父级菜单悬浮样式**/ .el-submenu__title:hover {color:#1890ff!important; } /** 父级菜单箭头悬浮样式**/ .el-submenu__title:hover>.el-submenu__icon-arrow{font-size: 13px!important;} /** 子菜单悬浮样式**/ .el-menu-item:hover{color:#1890ff!important; } /*…

什么是Jmeter ?Jmeter使用的原理步骤是什么?

1.1 什么是 JMeter Apache JMeter 是 Apache 组织开发的基于 Java 的压力测试工具。用于对软件做压力测试&#xff0c;它最初被设计用于 Web 应用测试&#xff0c;但后来扩展到其他测试领域。 它可以用于测试静态和动态资源&#xff0c;例如静态文件、Java 小服务程序、CGI 脚…

青藏高原1-km分辨率生态环境质量变化数据集(2000-2020)

青藏高原平均海拔4000米以上&#xff0c;人口1300万&#xff0c;是亚洲九大河流的源头&#xff0c;为超过15亿人口提供淡水、食物和其他生态系统服务&#xff0c;被誉为地球第三极和亚洲水塔。然而&#xff0c;在该地区的人与自然的关系的研究是有限的&#xff0c;尤其是在精细…

PgSQL-内核特性-TupleTableSlotOps

PgSQL-内核特性-TupleTableSlotOps 执行器中表达式结果、函数结果、投影结果等&#xff0c;各种结果都需要以元组的形式返回&#xff0c;所以PgSQL引入了一种通用格式保存数据&#xff1a;TupleTableSlot。PgSQL执行器将记录存储到“元组表”中在各个算子之间进行传递&#xff…

【神经网络可视化】 梯度上升,可视化工具,风格转移

可视化可以帮助我们更好的理解卷积网络每一层学到了什么&#xff0c;或者说每一个卷积核究竟学到了什么&#xff0c;他是怎么理解图像的 这种的话当我们神经网络结果不太好时&#xff0c;我们可以分析不好的原因 图片来源于李飞飞老师的内容 梯度上升方法做可视化 文章目录 …

BUUCTF reverse wp 21 - 30

[ACTF新生赛2020]rome 无壳, 直接拖进IDA32 y键把v2改成char[49], n键重命名为iuput int func() {int result; // eaxint v1[4]; // [esp14h] [ebp-44h]char input[49]; // [esp24h] [ebp-34h] BYREFstrcpy(&input[23], "Qsw3sj_lz4_Ujwl");printf("Please…

力扣 -- 44. 通配符匹配

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:bool isMatch(string s, string p) {int ms.size();int np.size();//为了调整映射关系s s;p p;//多开一行多开一列vector<vector<bool>> dp(m1,vector<bool>(n1,false));//初始化//dp[0]…

Mysql——三、SQL语句(上篇)

Mysql 一、SQL语句基础1、SQL简介2、SQL语句分类3、SQL语句的书写规范 二、数据库操作三、MySQL 字符集1、变量2、utf8和utf8mb4的区别 四、数据库对象五、SELECT语句1、简单的SELECT语句2、SQL函数2.1 聚合函数2.2 数值型函数2.3 字符串函数2.4 日期和时间函数2.5 流程控制函数…

JAVA:实现Excel和PDF上下标

1、简介 最近项目需要实现26个小写字母的上下标功能,自己去网上找了所有Unicode的上下标形式,缺少一些关键字母,顾后面考虑自己创建上下标字体样式,以此来记录。 2、Excel Excel本身是支持上下标,我们可以通过Excel单元格的样式来设置当前字体上下标,因使用的是POI的m…

Object.defineProperty()方法详解,了解vue2的数据代理

假期第一篇&#xff0c;对于基础的知识点&#xff0c;我感觉自己还是很薄弱的。 趁着假期&#xff0c;再去复习一遍 Object.defineProperty(),对于这个方法&#xff0c;更多的还是停留在面试的时候&#xff0c;面试官问你vue2和vue3区别的时候&#xff0c;不免要提一提这个方法…

【知识点】JavaScript中require的一些理解

以下内容源自个人理解&#xff0c;若有错误欢迎指出。 猜想 多个文件中require同一个文件时&#xff0c;对于首次出现的require&#xff0c;会去读取文件并执行一遍&#xff0c;然后加入缓存&#xff1b;之后当再次require到这个文件时&#xff0c;只会指向这个缓存&#xff0c…

Django(21):使用Celery任务框架

目录 Celery介绍Celery安装Celery使用项目文件和配置启动Celery编写任务调用异步任务查看任务执行状态及结果 设置定时和周期性任务配置文件添加任务Django Admin添加周期性任务启动任务调度器beat Flower监控任务执行状态Celery高级用法与注意事项给任务设置最大重试次数不同任…

px4的gazebo仿真相机模型报错解决办法,返回值256

&#x1f449;事情起因&#xff1a;我想做关于PX4无人机的摄像头仿真&#xff0c;根据PX4的官网文件 Tools/sitl_gazebo文件夹里面有对应的模型可以使用&#xff0c;我就想在mavros_posix_sitl文件里面修改vehicle参数&#xff0c;比如直接将vehicle“iris_stereo_camera”。然…

PyTorch 模型性能分析和优化 — 第 1 部分

一、说明 这篇文章的重点将是GPU上的PyTorch培训。更具体地说&#xff0c;我们将专注于 PyTorch 的内置性能分析器 PyTorch Profiler&#xff0c;以及查看其结果的方法之一&#xff0c;即 PyTorch Profiler TensorBoard 插件。 二、深度框架 训练深度学习模型&#xff0c;尤其是…

Springboot中使用拦截器、过滤器、监听器

一、Servlet、Filter&#xff08;过滤器&#xff09;、 Listener&#xff08;监听器&#xff09;、Interceptor&#xff08;拦截器&#xff09; Javaweb三大组件&#xff1a;servlet、Filter&#xff08;过滤器&#xff09;、 Listener&#xff08;监听器&#xff09; Spring…

嵌入式Linux应用开发-面向对象-分层-分离及总线驱动模型

嵌入式Linux应用开发-面向对象-分层-分离及总线驱动模型 第八章 驱动设计的思想&#xff1a;面向对象/分层/分离8.1 面向对象8.2 分层8.3 分离8.4 写示例代码8.5 课后作业 第九章 驱动进化之路&#xff1a;总线设备驱动模型9.1 驱动编写的 3种方法9.1.1 传统写法9.1.2 总线设备…

QT信号槽

目录 信号槽的概念 按钮的常用信号 自定义槽函数 自定义信号函数 自定义槽和信号注意的事项 信号与槽的拓展 lambda表达式 信号槽的概念 信号槽是Qt框架引以为豪的机制之一。所谓信号槽&#xff0c;实际就是观察者模式。当某个事件发生之后&#xff0c;比如&#xff0c…

布隆过滤器Bloom Filter

本章代码gitee仓库&#xff1a;布隆过滤器 文章目录 0. 前言1. 布隆过滤器的概念2. 布隆过滤器的实现2.1 哈希函数2.2 插入和判断 3. 布隆过滤器的删除4. 布隆过滤器的误判 0. 前言 我们在玩某款游戏的时候&#xff0c;刚注册的话&#xff0c;我们需要取一个昵称&#xff0c;这…

【Java】复制数组的四种方式

1. System.arraycopy() 用来将一个数组的&#xff08;一部分&#xff09;内容复制到另一个数组里面去。 定义&#xff1a; void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);例&#xff1a; int[] arr1 { 1, 2, 3, 4, 5 }; int[] arr2 new…

CSS详细基础(四)显示模式

本帖开始介绍CSS中更复杂的内容 目录 一.显示模式 1.行内元素 2.块级元素 3.行内块元素 二.背景样式 一.显示模式 顾名思义&#xff0c;在CSS中&#xff0c;元素主要有3种显示模式&#xff1a;行内元素、块级元素、行内块元素~ 所谓块级元素&#xff0c;指的是该元素在…