springboot 配置多数据源以及动态切换数据源

news2025/1/24 19:58:51

场景

我们springboot项目,通常会有多个数据库,例如mysql,vertica,postgresql等等数据库,通常我们需要动态切换使用我们想要的数据库,这时候就需要配置多数据源了

多数据源特性

支持多数据库类型:例如,同时连接 MySQL、PostgreSQL 和 Vertica 等不同数据库,在应用中能够根据不同的业务需求切换到对应的数据库。

动态数据源切换:根据请求、业务逻辑或用户上下文,动态切换数据源(例如,按用户请求切换到不同的数据库),而不需要重启应用。

数据库隔离:不同业务模块使用不同的数据源,确保它们的数据完全隔离。例如,一个模块使用 MySQL,另一个模块使用 Vertica。

高可用和容错:通过配置多个数据源,可以在主数据库发生故障时自动切换到备用数据库,提升系统的高可用性。

实现

基于springboot3.4配置

引入依赖的方式

添加依赖
        <!--动态数据源-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>4.3.0</version>
        </dependency>
配置YML文件
spring:
  datasource:
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为 master
      primary: master
      datasource:
        master:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
          username: root
          password: 123456
        slave:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/eshopping?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
          username: root
          password: 123456
调用数据库

直接再类上加上@DS(“master”)注解即是连接的master数据库

@Service
@DS("master")
public class LoginServiceImpl implements LoginService{
问题

博主这里遇见了问题,因为我配置了JWT+SpringSecurity,这里报错了,信息如下,找不到数据源,猜想应该是和JwtAuthenticationFilter和数据源的加载顺序有关

2025-01-21 21:43:37.097 ERROR --- [           main] o.s.b.web.embedded.tomcat.TomcatStarter  : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'jwtAuthenticationFilter': Unsatisfied dependency expressed through field 'userDetailsService': Error creating bean with name 'customUserDetailsService': Unsatisfied dependency expressed through field 'loginMapper': Error creating bean with name 'loginMapper' defined in file [E:\IDE\demoNew\target\classes\com\example\demonew\mapper\loginMapper\LoginMapper.class]: Cannot resolve reference to bean 'sqlSessionTemplate' while setting bean property 'sqlSessionTemplate'
2025-01-21 21:43:37.134 INFO  --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2025-01-21 21:43:37.155 WARN  --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server
2025-01-21 21:43:37.167 INFO  --- [           main] .s.b.a.l.ConditionEvaluationReportLogger : 

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2025-01-21 21:43:37.210 ERROR --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class


Action:

Consider the following:
	If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
	If you have database settings to be loaded from a particular profile you may need to activate it (the profiles dev are currently active).

手动实现

依赖默认已经引入,就是基础的mysql,mybatis依赖

配置YML文件
spring:
  datasource:
    master:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
      username: root
      password: 123456
    slave:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://localhost:3306/eshopping?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
      username: root
      password: 123456
配置DataSourceContextHolder

创建DataSourceContextHolder 类通过ThreadLocal存储数据源信息

/*DataSourceContextHolder 类用于保存当前线程的数据库标识,通常使用 ThreadLocal 来保存当前的数据源。*/
public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

    public static void clearDataSourceType() {
        contextHolder.remove();
    }
}
创建DynamicDataSource

创建DynamicDataSource 类继承AbstractRoutingDataSource ,可获取当前线程数据源信息

/*DynamicDataSource 类继承自 AbstractRoutingDataSource,用于根据当前线程的上下文动态选择数据源。*/
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        // 获取当前线程中的数据源标识
        return DataSourceContextHolder.getDataSourceType();
    }
}
配置DataSourceConfig

配置DataSourceConfig类,将配置文件的中的数据源配置成bean,然后存入DataSourceContextHolder 类中的ThreadLocal中存储

/*配置一个 DynamicDataSource,将多个 DataSource 放入其中,并设置默认数据源。*/
@Configuration
public class DataSourceConfig {

    @Primary
    @Bean(name = "masterDataSource")
    @ConfigurationProperties("spring.datasource.master")
    public HikariDataSource primaryDataSource() {
        return new HikariDataSource();
    }

    @Bean(name = "slaveDataSource")
    @ConfigurationProperties("spring.datasource.slave")
    public HikariDataSource secondaryDataSource() {
        return new HikariDataSource();
    }

    @Bean
    public DataSource dataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("masterDataSource", primaryDataSource());
        targetDataSources.put("slaveDataSource", secondaryDataSource());

        DynamicDataSource routingDataSource = new DynamicDataSource();
        routingDataSource.setDefaultTargetDataSource(primaryDataSource());
        routingDataSource.setTargetDataSources(targetDataSources);

        return routingDataSource;
    }

}
使用

在serviceImpl中使用
主要是用下面的方法切换数据源DataSourceContextHolder.setDataSourceType(“masterDataSource”);

@Service
public class LoginServiceImpl implements LoginService{

    @Autowired
    private LoginMapper loginMapper;

    @Override
    public String register(String username, String password) {
        return "成功";
    }

    @Override
    public String login(String username, String password) {
        DataSourceContextHolder.setDataSourceType("masterDataSource");
        String pw = loginMapper.login(username);
        if(password.equals(pw)){
            return "成功";
        }else {
            return "失败";

        }
    }


}
清除

ThreadLocal不使用之后需要remove,不然容易造成数据污染或错误

原因

ThreadLocal 是与线程绑定的,在 Spring 或类似的框架中,线程池通常会重用线程,如果在请求处理过程中没有清理 ThreadLocal 中的内容,接下来复用的线程可能会带着之前请求中的 ThreadLocal 数据继续处理新的请求,从而导致数据污染或错误

假设你有两个数据源:primary 和 secondary,当请求 A 处理时,ThreadLocal 设置了 primary 数据源。
如果线程 A 继续处理请求 B,但此时没有清理 ThreadLocal,那么请求 B 可能会意外地使用 primary 数据源,而不是它本该使用的 secondary 数据源。

手动实现(注解版本)推荐

上面的版本每次都需要DataSourceContextHolder.setDataSourceType(“masterDataSource”);切换数据源非常麻烦,因此可以自定义注解

自定义注解

使用@DataSource时默认值masterDataSource

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    String value() default "masterDataSource"; // 默认是 master 数据源
}

定义AOP切面

在使用该注解前将值赋值给DataSourceContextHolder中ThreadLocal存储,并可以方便的清除数据源

@Aspect
@Component
public class DataSourceAspect {

    @Before("@annotation(dataSource)")
    public void switchDataSource(DataSource dataSource) {
        String dataSourceType = dataSource.value();
        DataSourceContextHolder.setDataSourceType(dataSourceType);
    }

    @After("@annotation(dataSource)")
    public void clearDataSource(DataSource dataSource) {
        DataSourceContextHolder.clearDataSourceType();
    }

    @AfterThrowing("@annotation(dataSource)")
    public void handleException(DataSource dataSource) {
        DataSourceContextHolder.clearDataSourceType();
    }
}
使用
@Service
public class LoginServiceImpl implements LoginService{

    @Autowired
    private LoginMapper loginMapper;

    @Override
    public String register(String username, String password) {
        return "成功";
    }

    @Override
    @DataSource("slaveDataSource")
    public String login(String username, String password) {
        String pw = loginMapper.login(username);
        if(password.equals(pw)){
            return "成功";
        }else {
            return "失败";

        }
    }


}

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

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

相关文章

大模型 / 智能体在智能运维领域的应用总结与发展趋势概述

智能体 智能运维 &#xff1f; 回顾大模型的发展 大模型的发展在过去两年间呈现出爆炸式的增长&#xff0c;成为推动人工智能领域快速进步的关键力量。 2023年3月&#xff1a;百度发布了其知识增强的大语言模型产品“文心一言”&#xff0c;这标志着国内AI大模型产业竞争的…

戴尔电脑设置u盘启动_戴尔电脑设置u盘启动多种方法

最近有很多网友问&#xff0c;戴尔台式机怎么设置u盘启动&#xff0c;特别是近两年的戴尔台式机比较复杂&#xff0c;有些网友不知道怎么设置&#xff0c;其实设置u盘启动有两种方法&#xff0c;下面小编教大家戴尔电脑设置u盘启动方法。 戴尔电脑设置u盘启动方法一、戴尔进入b…

【博客之星】年度总结:在云影与墨香中探寻成长的足迹

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、年度回顾 1、创作历程 2、个人成长 3、个人生活与博客事业 二、技术总结 1、赛道选择 2、技术工具 3、实战项目 三、前景与展望 1、云原生未来…

《安富莱嵌入式周报》第349期:VSCode正式支持Matlab调试,DIY录音室级麦克风,开源流体吊坠,物联网在军工领域的应用,Unicode字符压缩解压

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版&#xff1a; 《安富莱嵌入式周报》第349期&#xff1a;VSCode正式支持Matlab调试&#xff0c;DIY录音室级麦克风…

cursor ide配置远程ssh qt c++开发环境过程记录

cursor是啥就不介绍了&#xff0c;好像是目前最好用的ai ide&#xff0c;下面主要是配置远程ssh连接linux机器进行qt5 c程序运行的配置过程记录。 一、c_cpp_properties.json 在项目根目录的.vscode目录里面新建c_cpp_properties.json文件&#xff0c;根据你的实际情况配置该文…

Langchain+文心一言调用

import osfrom langchain_community.llms import QianfanLLMEndpointos.environ["QIANFAN_AK"] "" os.environ["QIANFAN_SK"] ""llm_wenxin QianfanLLMEndpoint()res llm_wenxin.invoke("中国国庆日是哪一天?") print(…

lwIP——3 内存管理

目录 1.什么是内存管理 2.lwIP内存堆 3.lwIP内存堆程序代码解析 3.1 mem_init程序解析 3.2 mem_malloc程序解析 3.3 mem_free程序解析 4.lwIP内存池 5.lwIP内存池程序代码解析 5.1 实现lwIP内存池的文件 5.1.1 memp_priv.h 5.1.2 memp_std.h 5.1.3 memp.h memp_t 枚…

使用 Serilog 在 .NET Core 6.0 中日志记录

在本文中&#xff0c;我们将讨论日志记录的基础知识以及在 .NET Core 6 中使用 Serilog 的逐步实现。 先决条件 1、Visual Studio 2022。 2、C# 和 .NET Core 的基础知识。 3、.NET Core 6 SDK。 日志记录基础知识 从技术角度来说&#xff0c;日志记录是记录事件并捕获应用程…

当 Facebook 窥探隐私:用户的数字权利如何捍卫?

随着社交平台的普及&#xff0c;Facebook 已经成为全球用户日常生活的一部分。然而&#xff0c;伴随而来的隐私问题也愈发严峻。近年来&#xff0c;Facebook 频频被曝出泄露用户数据、滥用个人信息等事件&#xff0c;令公众对其隐私保护措施产生质疑。在这个信息化时代&#xf…

微信小程序隐藏右侧胶囊按钮,分享和关闭即右侧三个点和小圆圈按钮

在微信小程序开发过程中&#xff0c;可能需要将右侧的胶囊按钮、即右侧的三个点和小圆圈按钮关闭掉。如图&#xff1a; 这时&#xff0c;我们只需在该页面的json文件中进行相关配置即可 {"navigationBarTitleText": "商品详情页","navigationStyle&q…

Sharding-JDBC 5.4.1+SpringBoot3.4.1+MySQL8.4.1 使用案例

最近在升级 SpringBoot 项目&#xff0c;原版本是 2.7.16&#xff0c;要升级到 3.4.0 &#xff0c;JDK 版本要从 JDK8 升级 JDK21&#xff0c;原项目中使用了 Sharding-JDBC&#xff0c;版本 4.0.0-RC1&#xff0c;在升级 SpringBoot 版本到 3.4.0 之后&#xff0c;服务启动失败…

鸿蒙仓颉环境配置(仓颉SDK下载,仓颉VsCode开发环境配置,仓颉DevEco开发环境配置)

目录 ​1&#xff09;仓颉的SDK下载 1--进入仓颉的官网 2--点击图片中的下载按钮 3--在新跳转的页面点击即刻下载 4--下载 5--找到你们自己下载好的地方 6--解压软件 2&#xff09;仓颉编程环境配置 1--找到自己的根目录 2--进入命令行窗口 3--输入 envsetup.bat 4--验证是否安…

【数据库】详解MySQL数据库中索引的本质与底层原理

目录 1.MySQL索引的本质 1.1.索引的重要性 1.2.索引演示 1.3.索引的底层原理 1.3.1磁盘IO的原理 1.3.2.硬盘的主要结构 1.3.3.工作情形 1.3.4.各主要部件说明 1.3.5.扇区中是如何表示01数据的 2.MySQL索引底层原理 2.1.二叉查找树 2.2.平衡二叉查找树 2.3.B树和B树…

网络编程原理:回显服务器与客户端通信交互功能

文章目录 路由器及网络概念网络通信基础TCP/IP 五层协议封装和分用封装分用 网络编程&#xff08;网络协议&#xff09;UDP类 API使用实现回显通信程序回显服务器(UDP代码)回显客户端(UDP代码) TCP API使用回显服务器(TCP代码)回显客户端(TCP代码) 路由器及网络概念 网络发展是…

AIGC视频扩散模型新星:Video 版本的SD模型

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍慕尼黑大学携手 NVIDIA 等共同推出视频生成模型 Video LDMs。NVIDIA 在 AI 领域的卓越成就家喻户晓&#xff0c;而慕尼黑大学同样不容小觑&#xff0c;…

[Day 15]54.螺旋矩阵(简单易懂 有画图)

今天我们来看这道螺旋矩阵&#xff0c;和昨天发的题很类似。没有技巧&#xff0c;全是循环。小白也能懂~ 力扣54.螺旋矩阵 题目描述&#xff1a; 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; …

用Python绘制一只懒羊羊

目录 一、准备工作 二、Turtle库简介 三、绘制懒羊羊的步骤 1. 导入Turtle库并设置画布 2. 绘制头部 3. 绘制眼睛 4. 绘制嘴巴 5. 绘制身体 6. 绘制四肢 7. 完成绘制 五、运行代码与结果展示 六、总结 在这个趣味盎然的技术实践中,我们将使用Python和Turtle图形…

QT QTreeWidget控件 全面详解

本系列文章全面的介绍了QT中的57种控件的使用方法以及示例,包括 Button(PushButton、toolButton、radioButton、checkBox、commandLinkButton、buttonBox)、Layouts(verticalLayout、horizontalLayout、gridLayout、formLayout)、Spacers(verticalSpacer、horizontalSpacer)、…

掌握Spring事务隔离级别,提升并发处理能力

Spring框架支持的事务隔离级别与标准的JDBC隔离级别保持一致&#xff0c;共包括五大隔离级别&#xff0c;它们分别是&#xff1a;DEFAULT&#xff08;默认隔离级别&#xff09;、READ_UNCOMMITTED&#xff08;读未提交&#xff09;、READ_COMMITTED&#xff08;读已提交&#x…

Vue基础(2)

19、组件之间传递数据 组件与组件之间不是完全独立的&#xff0c;而是有交集的&#xff0c;那就是组件与组 件之间是可以传递数据的 传递数据的解决方案就是 props ComponentA.vue <template><!-- 使用ComponentB组件&#xff0c;并传递title属性 --><h3>…