SpringBoot实现多数据源切换快速入门

news2024/10/7 17:31:12

1. 概述


随着项目规模的扩大和业务需求的复杂化,单一数据源已经不能满足实际开发中的需求。在许多情况下,我们需要同时操作多个数据库,或者需要将不同类型的数据存储在不同的数据库中。这时,多数据源场景成为必不可少的解决方案。

市面上常见的多数据源实现方案如下:

  • 方案1:基于Spring框架提供的AbstractRoutingDataSource。

    • 优点: 简单易用,支持动态切换数据源;适用于少量数据源情况。
    • 场景:适用于需要动态切换数据源,且数据库较少的情况。
    • 文档地址:
  • 方案2:使用MP提供的Dynamic-datasource多数据源框架。

    • 文档地址:https://baomidou.com/guides/dynamic-datasource/#dynamic-datasource
  • 方案3:通过自定义注解在方法或类上指定数据源,实现根据注解切换数据源的功能。

    • 优点: 灵活性高,能够精确地控制数据源切换;在代码中直观明了。
    • 场景: 适用于需要在代码层面进行数据源切换,并对数据源切换有精细要求的情况。
  • 方案4:使用动态代理技术,在运行时动态切换数据源,实现多数据源的切换。

    • 优点: 灵活性高,支持在运行时动态切换数据源;适合对数据源切换的逻辑有特殊需求的情况。
    • 场景: 适用于需要在运行时动态决定数据源切换策略的情况。

2. 基于SpringBoot的多数据源实现方案


1、执行sql脚本:(分别创建两个数据库,里面都提供一张user表)

-- 创建数据库ds1
CREATE DATABASE `ds1`;

-- 使用ds1数据库
USE ds1;

-- 创建user表
CREATE TABLE `user` (
    `id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键id',
    `username` VARCHAR(50) COMMENT '用户名',
    `gender` TINYINT(1) COMMENT '性别:0男,1女'
);

-- 向user表插入数据
INSERT INTO user (username, gender) VALUES 
('张三', 1),
('李四', 0),
('王五', 1);

-- 创建数据库ds2
CREATE DATABASE `ds2`;

-- 使用ds2数据库
USE ds2;

-- 创建user表
CREATE TABLE `user` (
    `id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键id',
    `username` VARCHAR(50) COMMENT '用户名',
    `gender` TINYINT(1) COMMENT '性别:0男,1女'
);

-- 向user表插入数据
INSERT INTO user (username, gender) VALUES 
('赵六', 1),
('陈七', 0),
('宝国', 1);

2、创建一个maven工程,向pom.xml中添加依赖:

<!--锁定SpringBoot版本-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.10</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
    <!--jdbc起步依赖-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
    <!--test起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.20</version>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
      <!--jdbc起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
</dependencies>

3、编写实体类:

package cn.aopmin.entity;

import lombok.*;

/**
 * 实体类
 *
 * @author 白豆五
 * @since 2024/7/4
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
    private Integer id;
    private String username;
    private Integer gender;
}

4、创建application.yml文件,配置数据源:

spring:

  #动态数据源配置
  datasource:
    ds1:
      driverClassName: com.mysql.cj.jdbc.Driver
      jdbcUrl: jdbc:mysql://localhost:3306/ds1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
      username: root
      password: 123456
    ds2:
      driverClassName: com.mysql.cj.jdbc.Driver
      jdbcUrl: jdbc:mysql://localhost:3306/ds2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
      username: root
      password: 123456

logging:
  level:
    cn.aopmin: debug

5、编写数据源配置类:

package cn.aopmin.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 数据源配置类
 * 配置多数据源和动态数据源
 *
 * @author 白豆五
 * @since 2024/7/4
 */
@Configuration
public class DataSourceConfig {

    //定义数据源1
    @Bean("ds1")
    @ConfigurationProperties(prefix = "spring.datasource.ds1")
    public DataSource ds1() {
        return DataSourceBuilder.create().build();
    }

    //定义数据源2
    @Bean("ds2")
    @ConfigurationProperties(prefix = "spring.datasource.ds2")
    public DataSource ds2() {
        return DataSourceBuilder.create().build();
    }

    //定义动态数据源
    @Bean(name = "dataSource")
    public DataSource dynamicDataSource(@Qualifier("ds1") DataSource ds1,
                                        @Qualifier("ds2") DataSource ds2) {
        //1.定义数据源map
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("ds1", ds1);
        targetDataSources.put("ds2", ds2);
        //2.实例化自定义的DynamicDataSource对象, 并设置数据源map
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        //3.设置默认数据源,未匹配上则使用默认数据源
        dynamicDataSource.setDefaultTargetDataSource(ds1);
        return dynamicDataSource;
    }

    // 通过JdbcTemplate	
    @Bean
    public JdbcTemplate jdbcTemplate(@Qualifier("dataSource") DataSource ds) {
        return new JdbcTemplate(ds);
    }
}

6、创建DynamicDataSource动态数据类:

package cn.aopmin.config;

import cn.aopmin.common.DataSourceContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * AbstractRoutingDataSource(抽象的数据源路由器) 的基本原理是, 它维护了一个数据源的集合,每个数据源都有唯一的一个标识符
 * 当应用程序需要访问数据库的时候,AbstractRoutingDataSource会根据某种匹配规则(例如请求参数、用户身份等)来选择一个合适的数据源,
 * 并将请求转发给这个数据源。
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    /**
     * 获取数据源名称
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

7、定义一个ThreadLocal工具类:

package cn.aopmin.common;

/**
 * 使用ThreadLocal保存数据源名称
 *
 * @author 白豆五
 * @since 2024/7/4
 */
public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    // 将数据源名称绑定到当前线程上
    public static void setDataSource(String dataSourceName) {
        contextHolder.set(dataSourceName);
    }

    // 获取当前线程上的数据源名称
    public static String getDataSource() {
        return contextHolder.get();
    }

    // 清除数据源名称
    public static void clearDataSource() {
        contextHolder.remove();
    }
}

8、创建启动类

package cn.aopmin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 启动类
 *
 * @author 白豆五
 * @since 2024/7/3
 */
@SpringBootApplication
public class Demo01Application {
    public static void main(String[] args) {
        SpringApplication.run(Demo01Application.class, args);
    }
}

9、创建UserService:

package cn.aopmin.service;

import cn.aopmin.common.DataSourceContextHolder;
import cn.aopmin.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

/**
 * @author 白豆五
 * @since 2024/7/4
 */
@Service
public class UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insertDs1(User user) {
        try {
            // todo:自定义注解+SpringAop实现数据源的切换
            DataSourceContextHolder.setDataSource("ds1");
            String sql = "insert into user(username,gender) values(?,?)";
            jdbcTemplate.update(sql,user.getUsername(), user.getGender());
        } finally {
            DataSourceContextHolder.clearDataSource();
        }
    }

    public void insertDs2(User user) {
        try {
            DataSourceContextHolder.setDataSource("ds2");
            String sql = "insert into user(username,gender) values(?,?)";
            jdbcTemplate.update(sql,user.getUsername(), user.getGender());
        } finally {
            DataSourceContextHolder.clearDataSource();
        }
    }
}

10、编写测试:

package cn.aopmin.service;

import cn.aopmin.Demo01Application;
import cn.aopmin.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest(classes = {Demo01Application.class})
public class UserServiceTest {

    @Resource
    private UserService userService;


    @Test
    public void testInsertDs1() {
        User user = new User();
        user.setUsername("jack");
        user.setGender(0);
        user.setGender(1);
        userService.insertDs1(user);
    }

    @Test
    public void testInsertDs2() {
        User user = new User();
        user.setUsername("rose");
        user.setGender(1);
        userService.insertDs2(user);
    }
}

最终效果:

在这里插入图片描述


3. 基于Dynamic-datasource实现方案


mp文档:https://baomidou.com/guides/dynamic-datasource/#_top

1、创建SpringBoot工程,引入Dynamic-datasource依赖:

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

2、配置数据源:

spring:
  #多数据源配置
  datasource:
    dynamic:
      primary: master #设置默认数据源
      strict: false #是否严格检查动态数据源提供的数据库名
      datasource:
        #数据源1
        master:
          url: jdbc:mysql:///ds1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
        #数据源2
        slave1:
          url: jdbc:mysql:///ds2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver

3、实体类:

package cn.aopmin.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 实体类
 *
 * @author 白豆五
 * @since 2024/7/4
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String username;
    private Integer gender;
}

4、业务类:

package cn.aopmin.service;

import cn.aopmin.entity.User;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

/**
 * 通过@DS注解切换数据源
 * @author 白豆五
 * @since 2024/7/4
 */
@Service
// @DS("master") //不加@DS注解,会使用默认数据源
public class UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insertDs1(User user) {
        String sql = "insert into user(username,gender) values(?,?)";
        jdbcTemplate.update(sql,user.getUsername(), user.getGender());
    }

    @DS("slave1")
    public void insertDs2(User user) {
        String sql = "insert into user(username,gender) values(?,?)";
        jdbcTemplate.update(sql,user.getUsername(), user.getGender());
    }
}

4、测试类:

package cn.aopmin.service;


import cn.aopmin.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest
public class UserServiceTest2 {

    @Resource
    private UserService userService;


    @Test
    public void testInsertDs1() {
        User user = new User();
        user.setUsername("jack");
        user.setGender(0);
        user.setGender(1);
        userService.insertDs1(user);
    }

    @Test
    public void testInsertDs2() {
        User user = new User();
        user.setUsername("rose");
        user.setGender(1);
        userService.insertDs2(user);
    }
}

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

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

相关文章

C#的多线程UI窗体控件显示方案 - 开源研究系列文章

上次编写了《LUAgent服务器端工具》这个应用&#xff0c;然后里面需要新启动一个线程去对文件进行上传到FTP服务器&#xff0c;但是新线程里无法对应用主线程UI的内容进行更改&#xff0c;所以就需要在线程里设置主UI线程里控件信息的方法&#xff0c;于是就有了此博文。此文记…

录屏怎么打开?这3招请收好

在数字化飞速发展的今天&#xff0c;录屏功能已经不仅仅是一个简单的工具&#xff0c;而是成为了我们工作、学习和娱乐中的得力助手。但是&#xff0c;不同设备或系统打开录屏功能的方式可能有所不同&#xff0c;打开录屏功能的方式也在不断更新和演变&#xff0c;今天我们就来…

【前端项目笔记】8 订单管理

订单管理 效果展示&#xff1a; 在开发功能之前先创建分支order cls 清屏 git branch 查看所有分支&#xff08;*代表当前分支&#xff09; git checkout -b order 新建分支order git push -u origin order 将本地的当前分支提交到云端仓库origin中命名为order 通过路由方式…

“工控机”是什么?和普通电脑有区别嘛!

在现代工业生产中,有一种特殊的计算机,它不像普通电脑那样被放置于明亮的办公室内,而是常常藏身于机器轰鸣、环境恶劣的工厂车间里,这就是工控机——工业控制计算机的简称。作为工业自动化领域不可或缺的核心设备,工控机不仅承载着监控与数据采集(SCADA)、过程控制、数据…

LLM大模型安全概述

引言 2022年底以来&#xff0c;以ChatGPT为代表的大模型飞速发展&#xff0c;正在成为#驱动新质生产力发展#​的新动能、人类探索未知的新工具. 在显著提升人工智能(artificial intelligence, AI)模型通用理解和生成能力的同时&#xff0c;也带来了前所未有的安全风险. 大模型…

plugin:vite:import-analysis]No known conditions for“./lib/locale/lang/zh-cn“

将原有引入&#xff1a; import zhCn from element-plus/lib/locale/lang/zh-cn 改成&#xff1a; import zhCn from element-plus/es/locale/lang/zh-cn; 原因版本升级&#xff0c;引入路径改变&#xff08;原先的包在node_modules\element-plus\lib找不到&#xff09; 新…

数据预处理:统计关联性分析/数据清洗/数据增强/特征工程实例

专栏介绍 1.专栏面向零基础或基础较差的机器学习入门的读者朋友,旨在利用实际代码案例和通俗化文字说明,使读者朋友快速上手机器学习及其相关知识体系。 2.专栏内容上包括数据采集、数据读写、数据预处理、分类\回归\聚类算法、可视化等技术。 3.需要强调的是,专栏仅介绍主…

科技赋能智慧应急:“数字孪生+无人机”在防汛救灾中的应用

近期&#xff0c;全国多地暴雨持续&#xff0c;“麻辣王子工厂停工”“水上派出所成水上的派出所了”等相关词条冲上热搜&#xff0c;让人们看到了全国各地城市内涝、洪涝带来的严重灾情。暴雨带来的影响可见一斑&#xff0c;潜在的洪水、泥石流、山体滑坡等地质灾害更应提高警…

UartAssist 串口助手,存储文件 打开是乱码

问题描述 使用UartAssist 对采集的串口数据进行存储&#xff0c;存储为日志格式的文件&#xff0c;但打开的文件有些可正常显示&#xff0c;大部分打开是乱码&#xff1b; (串口输出无中文) 若打开的文本为 UTF-16&#xff0c;则为乱码&#xff0c;若是ANSI格式&#xff0c;则可…

【MySQL】库的操作【创建和操纵】

文章目录 1.创建数据库1.1字符集和校验规则1.查看系统默认字符集以及校验规则2.查看数据库支持的字符集以及校验规则 1.2校验规则对数据库的影响1.创建一个数据库&#xff0c;校验规则使用utf8_ general_ ci[不区分大小写]2.创建一个数据库&#xff0c;校验规则使用utf8_ bin[区…

HTML内容爬取:使用Objective-C进行网页数据提取

网页爬取简介 网页爬取&#xff0c;通常被称为网络爬虫或爬虫&#xff0c;是一种自动浏览网页并提取所需数据的技术。这些数据可以是文本、图片、链接或任何网页上的元素。爬虫通常遵循一定的规则&#xff0c;访问网页&#xff0c;解析页面内容&#xff0c;并存储所需信息。 …

继电器测试的价格和性价比如何?

继电器是广泛应用于各种电气控制系统中的开关元件&#xff0c;其主要功能是在输入信号的控制下&#xff0c;实现电路的断开和闭合。继电器的性能和质量直接影响到整个电气系统的稳定性和可靠性&#xff0c;因此对继电器进行严格的测试是非常必要的。那么&#xff0c;继电器测试…

软考-系统架构设计师[九年]上岸感想

2016年就开始参系统架构设计师的考试了&#xff0c;经历七次考试终于成功上岸&#xff0c;分享下自己这么多次考试失败的经验&#xff0c;希望大家可以少踩坑&#xff0c;一次通过考试 重点 如果你不想继续读下去&#xff0c;看完这段就行。 1.一定要知道最新的考试范围&…

VTK- 面绘制体绘制

在VTK中&#xff0c;面绘制&#xff08;Surface Rendering&#xff09;和体绘制&#xff08;Volume Rendering&#xff09;是两种常见的三维数据可视化方法。面绘制和体绘制是计算机图形学中用于三维数据可视化的重要技术&#xff0c;尤其在医学成像、科学可视化和计算机辅助设…

03.C1W2.Sentiment Analysis with Naïve Bayes

目录 Probability and Bayes’ RuleIntroductionProbabilitiesProbability of the intersection Bayes’ RuleConditional ProbabilitiesBayes’ RuleQuiz: Bayes’ Rule Applied Nave Bayes IntroductionNave Bayes for Sentiment Analysis P ( w i ∣ c l a s s ) P(w_i|clas…

OWASP ZAP安全扫描工具,扫描网站

OWASP ZAP (Zed Attack Proxy) 是一个功能强大的开源 web 应用程序安全扫描工具&#xff0c;用于发现和利用 web 应用程序中的漏洞。以下是如何安装和使用 OWASP ZAP 的步骤。 安装 OWASP ZAP 使用 Snap 安装&#xff1a; 在 Ubuntu 22.04 上可以通过 Snap 安装 OWASP ZAP&…

打造商贸物流“产-供-销”、“仓-运-配”全流程供应链

在当今全球化的商业环境中&#xff0c;商贸物流平台的搭建成为企业提升效率、降低成本并增强市场竞争力的关键因素。在现代商业环境中&#xff0c;商贸与物流之间的紧密协作是业务成功的关键因素。然而&#xff0c;许多组织面临着信息不对称、资源配套不足、以及系统间隔离等痛…

设计模型 - 学习笔记

学习参考&#xff1a; https://blog.csdn.net/m0_65346405/article/details/136994128 《系统分析师教程》 《设计模式之禅》 一. 设计模式的5大原则 1. 单一职责原则 一个类应该只有一个变化因子。 就是说&#xff0c;一个类要变化&#xff0c;比如增加功能&#xff0c;那么引…

DllImport进阶:参数配置与高级主题探究

深入讨论DllImport属性的作用和配置方法 在基础篇中&#xff0c;我们已经简单介绍了DllImport的一些属性。现在我们将深入探讨这些属性的实际应用。 1. EntryPoint EntryPoint属性用于指定要调用的非托管函数的名称。如果托管代码中的函数名与非托管代码中的函数名不同&#…

如何使用小红书矩阵系统:提升内容管理与发布的指南

小红书作为一个集社区分享与电商功能于一体的平台&#xff0c;吸引了大量的用户和创作者。随着内容创作和账号管理的复杂性增加&#xff0c;小红书矩阵系统成为了一个强大的工具&#xff0c;帮助用户提高效率和扩大影响力。本文将详细介绍如何使用小红书矩阵系统&#xff0c;以…