SpringBoot:基于SpringBoot实现多数据源动态切换

一、前言

  Spring Boot 多数据源,也称为动态数据源,是指在Spring Boot项目中配置多个数据源,以支持不同的数据库访问需求。这种配置方式在项目开发逐渐扩大、单个数据源无法满足项目支撑需求时变得尤为重要。

二、目的

配置多数据源的主要目的是为了满足复杂的业务需求,例如:

  ①. 当一个系统需要集成多个项目时,配置多数据源可以方便地管理不同项目的数据。

  ②. 当系统/服务需要对接多个系统,存入多个不同的表时,多数据源配置可以确保数据的安全性和隔离性。

  ③. 在读写分离的场景中,主库负责增删改操作,从库负责查询操作,通过多数据源配置可以提高系统的性能和稳定性。

三、实现原理

源码展示
在这里插入图片描述
  就是通过determineTargetDataSource中的determineCurrentLookupKey,它是个抽象方法,我们可以继承这个抽象类,去实现多数据源的切换。
在这里插入图片描述

三、代码示例

1. 在application.properties中配置多个数据源的数据库

datasource.master.type=com.alibaba.druid.pool.DruidDataSource
datasource.master.driver-class-name=com.mysql.jdbc.Driver
datasource.master.jdbc-url=jdbc:mysql://127.0.0.1:3306/mysql?relaxAutoCommit=true&zeroDateTimeBehavior=convertToNull&characterEncoding=utf-8&useSSL=false
datasource.master.username=root
datasource.master.password=root


datasource.slave.type=com.alibaba.druid.pool.DruidDataSource
datasource.slave.driver-class-name=com.mysql.jdbc.Driver
datasource.slave.jdbc-url=jdbc:mysql://127.0.0.1:3306/mysql2?relaxAutoCommit=true&zeroDateTimeBehavior=convertToNull&characterEncoding=utf-8&useSSL=false
datasource.slave.username=root
datasource.slave.password=root

dataSourceConfig.use=true

2. 定义数据源枚举类

package com.example.yddemo.RoutingData;

public enum DataSourceType {

    MASTER("master"), SLAVE("slave");

    String value;

    private DataSourceType(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

3. 定义一个配置类,加载我们的自定义属性值

@Configuration
@ConditionalOnProperty(name = "dataSourceConfig.use", havingValue = "true")
public class DataSourceConfig {

    @Bean("masterDatasource")
    @ConfigurationProperties(prefix = "datasource.master")
    public DataSource defaultDataSource(){
        return DataSourceBuilder.create().build();
    }

    @Bean("slaveDatasource")
    @ConfigurationProperties(prefix = "datasource.slave")
    public DataSource slaveDataSource(){
        return DataSourceBuilder.create().build();
    }

    @Bean
    @DependsOn({"masterDatasource", "slaveDatasource"})
    @Primary
    public DynamicDataSource dataSource(DataSource masterDatasource, DataSource slaveDatasource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.value, masterDatasource);
        targetDataSources.put(DataSourceType.SLAVE.value, slaveDatasource);
        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSources);
        dataSource.setDefaultTargetDataSource(masterDatasource);
        return dataSource;
    }

    @Bean(name = "slaveTransactionManager")
    public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

4. 定义数据源切换处理类

定义一个类让它继承AbstractRoutingDataSource,并重写determineCurrentLookupKey

public class DynamicDataSource extends AbstractRoutingDataSource {

    //用来保存数据源与获取数据源
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public DynamicDataSource() {
    }

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<String, DataSource> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(new HashMap<Object, Object>(targetDataSources));
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    /**
     * 创建切换数据源处理类
     * @param dataSource
     */
    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

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

    public static void clearDataSource() {
        contextHolder.remove();
    }

}

5. 为了方便使用,我们自定义注解

@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

    String name() default "";

}

6. 使用AOP,让我们的自定义注解生效

@Aspect
@Component
public class DataSourceAspect implements Ordered {

    @Pointcut("@annotation(com.example.yddemo.RoutingData.DataSource)")
    public void dataSourcePointCut() {
    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        try {
            DataSource dataSource = method.getAnnotation(DataSource.class);
            if (dataSource == null) {
                DynamicDataSource.setDataSource(DataSourceType.MASTER.getValue());
            } else {
                DynamicDataSource.setDataSource(dataSource.name());
            }
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }

}

7. 定义service方法测试,使用自定义注解

实体类

实体类
public class User {

    private Integer id;

    private String name;

    private String phone;

    private String email;
    
    // ... get  set
}

Mapper接口

@Mapper
public interface UserMapper {

    User getUserInfo(Integer id);

}

UserMapper.xml

<mapper namespace="com.example.yddemo.mapper.UserMapper" >

  <select id="getUserInfo" resultType="com.example.yddemo.RoutingData.User" >
    select
    id,name,phone,email
    from sys_user where id = #{id}
  </select>
  
</mapper>

Service实现类

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUserInfo(Integer id) {
        User userInfo = userMapper.getUserInfo(id);
        return userInfo;
    }

    @Override
    @DataSource(name = "slave")
    public User getSlaveUserInfo(Integer id) {
        return userMapper.getUserInfo(id);
    }
}

8. Controller里面进行方法调用

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/user/query")
    public User userQuery(Integer id) {
        // 业务逻辑
        return userService.getUserInfo(id);
    }


    @GetMapping("/user/slave/query")
    public User userQuery1(Integer id) {
        // 业务逻辑
        return userService.getSlaveUserInfo(id);
    }

}

注意:启动类上面加上@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}), 关闭 spring 的自动数据源配置

9. 运行结果
在这里插入图片描述

从库数据展示在这里插入图片描述

  以上就是关于多数据源切换的全部内容,有AOP,自定义注解,自定义属性配置等方面的知识点。希望对你有所帮助。

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

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

相关文章

FinRobot:一个由大型语言模型(LLM)支持的新型开源AI Agent平台,支持多个金融专业AI Agent

财务分析一直是解读市场趋势、预测经济结果和提供投资策略的关键。这一领域传统上依赖数据&#xff0c;但随着时间的推移&#xff0c;越来越多地使用人工智能&#xff08;AI&#xff09;和算法方法来处理日益增长的复杂数据。AI在金融领域的作用显著增强&#xff0c;它自动化了…

vm:为虚拟机配置多个虚拟网卡(ubuntu20.04)

前言&#xff1a; 环境&#xff1a;虚拟机 ubuntu 20.04 要求&#xff1a;如标题&#xff0c;但是这里针对的是 ubuntu 20.04&#xff0c;对于其他操作系统&#xff0c;可以找一下其他操作系统对应的配置文件是什么 vm 添加虚拟网卡 首先进入 vm&#xff1a; 点击设置&#xf…

【AI大模型】如何让大模型变得更聪明?基于时代背景的思考

【AI大模型】如何让大模型变得更聪明 前言 在以前&#xff0c;AI和大模型实际上界限较为清晰。但是随着人工智能技术的不断发展&#xff0c;基于大规模预训练模型的应用在基于AI人工智能的技术支持和帮助上&#xff0c;多个领域展现出了前所未有的能力。无论是自然语言处理、…

EPSON爱普生RTC RA8900CE/RA8000CE+松下Panasonic电池组合

RTC是一种实时时钟&#xff0c;用于记录和跟踪时间&#xff0c;具有独立供电和时钟功能。在某些应用场景中&#xff0c;为了保证RTC在断电或者其他异常情况下依然能够正常工作&#xff0c;需要备份电池方案来提供稳定的供电。本文将介绍EPSON爱普生RTC RA8900CE/RA8000CE松下Pa…

laravel项目配置Facades Redis自动补全,方法查看

问题原因: 因为Laravel的Redis连接实例是通过RedisManger的工厂类创建的,返回的是一个mixin的类型,因此在IDE中不能自动补全Redis的方法,缺少这个功能,使用起来有些麻烦,尤其是Redis有数十个方法,每个方法也有不少参数。 相关部分的代码如下: /*** @mixin \Illumina…

super().__init__() 来自于哪个PEP

super() 的简化用法&#xff08;即不带参数的 super()&#xff09;是在 Python 3 中引入的。这个改进是由 PEP 3135 规范化的。 PEP 3135: New Super PEP 3135 标题为 “New Super”。它介绍了一种新的方式来调用超类的方法&#xff0c;即不再需要显式地传递当前类和实例&…

格雷希尔GripSeal:G60P系列快速连接器,解决外螺纹无损伤连接的密封方案

在家电、卫浴行业的生产中&#xff0c;外螺纹连接往往面临着严苛的工作环境和复杂的使用场景;比如洗衣机、热水器、乃至龙头阀的一些塑料螺纹、铝合金螺纹和铜管螺纹&#xff0c;它们都需要一种既安全又无损的连接方式。 传统的连接方式会对这些螺纹造成刮伤&#xff0c;影响了…

【MyBatis】MyBatis操作数据库(一)

目录 MyBatis的基础定义MyBatis配置相关文件一、注解操作数据库1.1 Insert(插入注解)1.2 Delete(删除注解)1.3 Update(修改注解)1.4 Select(重点&#xff1a;查询注解)注解解决查询不匹配问题拓展&#xff1a;Param(重命名注解)和OPtions(自增注解) 二、 XML操作数据库2.1 xml实…

HTML语义化标签

<header> 主要用于网页整体顶部&#xff0c;<article>头部&#xff0c;<section>头部 <nav> 导航&#xff0c;一般有主要导航&#xff0c;路径导航&#xff0c;章节导航&#xff0c;内容目录导航 <main> 网页主要区域&#xff0c;一般一个网页…

2024 RCTF WebMisc部分 WP

Misc gogogo 考点:内存取证 得到 gogogo.raw 内存取证的题用volatility和AXIOM结合分析 AXIOM 分析存在云服务 但是百度网盘要密码 https://pan.baidu.com/share/init?surlZllFd8IK-oHvTCYl61_7Kw 发现访问过sqlite数据库 可以尝试提取数据库文件出来 结合 volatility 第…

Spring Security3.0版本

前言&#xff1a; 核心&#xff1a; A >> &#xff1f; >> B &#xff1f;代表判断层&#xff0c;由Security实现 这是之前的版本浓缩&#xff0c;现在3.0版本添加了更匹配的内容描写&#xff0c;匹配了mvc模式 非mvc模式 核心&#xff1a;client&#x…

【仿真成品设计】基于STM32的直流电机控制器Proteus仿真

基于STM32的直流电机控制器Proteus仿真 所需器件&#xff1a; Proteus版本&#xff1a;8.15 整体功能&#xff1a; 直流无刷电机控制器是基于ST公司的STM32F103芯片的基础上使用标准库进行开发设计的。选用L298芯片来控制直流电机&#xff1b;采用使用0.96寸OLED屏幕做直流…

振弦采集仪在岩土工程固结沉降监测中的应用研究

振弦采集仪在岩土工程固结沉降监测中的应用研究 岩土工程固结沉降是指土体在受到外力作用下&#xff0c;由于土体颗粒之间的重排结构&#xff0c;导致土体体积缩小和沉降的过程。固结沉降的监测对于岩土工程的设计和施工具有重要的意义&#xff0c;而振弦采集仪作为一种先进的…

主线程等待所有线程结束之后再执行

如何让主线程等待所有线程结束之后再执行 1、Future的机制&#xff0c;使用Future.get()阻塞等待结果&#xff08;Future&#xff0c;FutureTask&#xff09; 2、CountDownLatch同步工具类&#xff0c;此类的作用就是一个线程等待所有线程结束之后再执行 3、CompletableFuture …

使用Python爬取华为市场游戏类APP应用

文章目录 1. 写在前面2. 接口分析3. 爬虫开发4. 下载链接获取 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守…

统计信号处理-匹配滤波器实现与验证(matlab仿真)

什么是匹配滤波器 匹配滤波器是一种信号处理技术&#xff0c;它用于从噪声中提取信号&#xff0c;特别是在信号与噪声比率较低的情况下。匹配滤波器之所以存在&#xff0c;是因为它在信号检测和估计方面具有几个关键的优势&#xff1a; 最大化信噪比&#xff1a;匹配滤波器设计…

JVS低代码表单消息提示:从站内信到钉钉全覆盖,适应各类应用场景

表单消息提示配置 表单消息发送方式分为站内信、公众号、企业微信、钉钉和邮件。 站内信发送是指系统内部之间发送消息&#xff0c;用户登录系统后以弹窗形式在桌面右下角提示。 公众号发送消息则是用户在系统个人中心绑定微信后通过公众号接收消息。 企业微信、钉钉和邮件…

【Docker】宝塔创建Docker容器配置nginx

前言 本篇是我入门docker的第一篇&#xff0c;由于docker具有很好的移植性&#xff0c;易于安装&#xff0c;开箱即用&#xff1b;签约的公司项目开发需要我进行学习&#xff0c;否则money减半&#xff0c;5555~ 百度找了一圈&#xff0c;只有关于docker怎么装宝塔服务器的却没…

运算符重载(下)

目录 前置和后置重载前置的实现Date& Date::operator()代码 后置的实现Date Date::operator(int )代码 前置--和后置--重载前置--的实现Date& Date::operator--( )代码 后置--的实现Date Date::operator--(int )代码 流插入运算符重载流插入运算符重载的实现流提取运算…

1、css3 动态button展示学习

效果图&#xff1a; 1、首先创建html代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title…