Ruo-Yi 前后端分离如何不使用注解@DataSource的方式而是使用Mybatis插件技术实现多数据源的切换【可以根据配置文件进行开启/关闭】

news2024/9/26 5:17:45

Ruo-Yi 前后端分离如何不使用注解@DataSource的方式而是使用Mybatis插件技术实现多数据源的切换【可以根据配置文件进行开启/关闭】

1、首先 配置文件:

# 数据源配置
spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        druid:
            # 主库数据源
            master:
                url: jdbc:mysql://localhost:3306/ry-vue-master?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: 123456
            # 从库数据源
            slave:
                # 从数据源开关/默认关闭
                enabled: true
                url: jdbc:mysql://localhost:3306/ry-vue-slave?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: 123456

配置多数据源,我们需要开启 slave 数据源。

2、定义多个数据源

/**
 * druid 配置多数据源
 *
 * @author ruoyi
 */
@Configuration
public class DruidConfig
{
    // 定义数据源1
    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    // 定义数据源2
    @Bean
    @ConfigurationProperties("spring.datasource.druid.slave")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
    public DataSource slaveDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    // 定义动态数据源
    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource)
    {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }

    /**
     * 设置数据源
     *
     * @param targetDataSources 备选数据源集合
     * @param sourceName 数据源名称
     * @param beanName bean名称
     */
    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
    {
        try
        {
            DataSource dataSource = SpringUtils.getBean(beanName);
            targetDataSources.put(sourceName, dataSource);
        }
        catch (Exception e)
        {
        }
    }

}

3、定义动态数据源

/**
 * 动态数据源
 * 应用直接操作的是 AbstractRoutingDataSource 的实现类,告诉 AbstractRoutingDataSource 访问哪个数据库,
 * 然后由 AbstractRoutingDataSource 从事先配置好的数据源中选择一个数据源,来访问对应的数据库。
 *
 * AbstractRoutingDataSource 属性:
 *      Map<Object, Object> targetDataSources;所有数据源【需指定】
 *      Object defaultTargetDataSource;默认数据源【需指定】
 *      Map<Object, DataSource> resolvedDataSources = targetDataSources
 *
 * @author ruoyi
 */
public class DynamicDataSource extends AbstractRoutingDataSource
{
    /**
     * 设置数据源
     * @param defaultTargetDataSource 默认数据源
     * @param targetDataSources 备选数据源
     */
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
    {
        // 设置默认数据源,未匹配则使用默认数据源
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }


    @Override
    protected Object determineCurrentLookupKey()
    {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

4、定义mybatis插件

package com.ruoyi.framework.datasource;

import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.ruoyi.common.enums.DataSourceType;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

@Intercepts({@Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
), @Signature(
        type = Executor.class,
        method = "update",
        args = {MappedStatement.class, Object.class}
)})
@Component
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") // 为了能根据配置文件,决定是否在容器中创建插件对象
public class MyDynamicDataSourcePlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("============================");
        System.out.println("Intercepted method: " + invocation.getMethod().getName());
        System.out.println("============================");

        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];

        String dataSource = SqlCommandType.SELECT == ms.getSqlCommandType() ? (DataSourceType.SLAVE.name()) : (DataSourceType.MASTER.name());
        com.ruoyi.framework.datasource.DynamicDataSourceContextHolder.setDataSourceType(dataSource);
        try {
            return invocation.proceed();
        } finally {
            com.ruoyi.framework.datasource.DynamicDataSourceContextHolder.clearDataSourceType();
        }

    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }
}


你以为完了????没有一定要将这个插件注册到sqlsessionfactory里面,我在这里遇到了很大的坑,因为若依自己写了一个sqlsessionfactory,所以要找到若依写的 SqlSessionFactory,然后将我们的插件加入进去。

若依在 MyBatisConfig 里面写了一个sqlSessionFactory()方法:

    @Value("${spring.datasource.druid.slave.enabled:false}") // 为了能根据配置文件,决定是否使用插件
    private boolean enableDynamicDataSource; 

    @Autowired(required = false) // 若没有开启slave数据元,容器中没有该对象了
    private MyDynamicDataSourcePlugin myDynamicDataSourcePlugin;

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource, MyDynamicDataSourcePlugin plugin) throws Exception
    {
        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
        String mapperLocations = env.getProperty("mybatis.mapperLocations");
        String configLocation = env.getProperty("mybatis.configLocation");
        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
        VFS.addImplClass(SpringBootVFS.class);

        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        if (enableDynamicDataSource && myDynamicDataSourcePlugin != null) {
            sessionFactory.setPlugins(new Interceptor[]{myDynamicDataSourcePlugin});
        }
        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
        sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
        return sessionFactory.getObject();
    }

根据配置文件,实现动态添加 数据源切换插件:

if (enableDynamicDataSource && myDynamicDataSourcePlugin != null) {
            sessionFactory.setPlugins(new Interceptor[]{myDynamicDataSourcePlugin});
        }

这个时候就可以测试了。

数据库当前状态:
master:
在这里插入图片描述
slave:
在这里插入图片描述
添加一个用户:
在这里插入图片描述

添加用户后数据库状态:
页面:在这里插入图片描述
发现没有 orm,没有就对了,因为查询查询的是 slave,添加用户添加进去的是 master库

master:
在这里插入图片描述

slave:
在这里插入图片描述

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

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

相关文章

ZooKeeper--基于Kubernetes部署ZooKeeper

ZooKeeper 服务 服务类型: 无头服务&#xff08;clusterIP: None&#xff09;&#xff0c;这是 StatefulSet&#xff08;有状态集&#xff09;必需的配置。 端口: 2181 (客户端): 用于客户端连接。 2888 (跟随者): 用于 ZooKeeper 服务器之间的连接。 3888 (领导者): 用于领导者…

邮政快递批量查询解决方案:提升业务运营效率

邮政快递批量查询&#xff1a;固乔快递查询助手的高效体验 在电商行业日益繁荣的今天&#xff0c;快递物流成为了连接商家与消费者的关键纽带。而对于需要处理大量订单的电商企业或个人而言&#xff0c;如何高效、准确地查询和跟踪快递物流信息显得尤为重要。幸运的是&#xf…

linux 云主机下载压缩包安装配置 maven 实录(华为云 EulerOS)

本想通过 yum install maven 直接安装的, 方便省事, 但报错说没找到, 于是只能手动安装了, 把整个过程记录了一下, 包括下载, 解压, 配置及验证的全过程, 并对用到的命令及参数作了详细说明, 需要的同学可以参考. maven 官网找到下载链接 首先要去到 maven 的官网, https://m…

OpenCV+Python自动填涂机读卡

接上一篇OpenCVPython识别机读卡-CSDN博客&#xff0c;既然可以识别机读卡填涂答案了&#xff0c;将标准答案绘制到机读卡上也就简单了。 工作原理 1.答题区域为整张图片最大轮廓&#xff0c;先找出答题区域。 2.答题区域分为6行&#xff0c;每行4组&#xff0c;第6行只有1组…

【Java设计模式】抽象文档模式:以灵活性简化数据处理

文章目录 抽象文档设计模式的意图抽象文档模式的详细解释及实际示例Java中抽象文档模式的编程示例抽象文档模式类图Java中何时使用抽象文档模式抽象文档模式的优点和权衡源码下载参考和致谢 抽象文档设计模式的意图 Java中的抽象文档设计模式是一种关键的结构设计模式&#xf…

【mysql集群之组复制】

目录 一、 mysql高可用之组复制 (MGR)组复制单主和多主模式实现mysql的组复制 二、 mysql-router&#xff08;mysql路由&#xff09;实现负载均衡 一、 mysql高可用之组复制 (MGR) MySQL Group Replication(简称 MGR )是 MySQL 官方于 2016 年 12 月推出的一个全新的高可用与高…

OpenHarmony南向开发:SmartPerf-Device使用说明

简介 SmartPerf 端是一款基于 OpenHarmony 系统开发的性能功耗测试工具&#xff0c;操作简单易用&#xff0c;可提供包括性能、功耗的关键 KPI 指标&#xff0c;给出具体指标的测试值&#xff0c;包括采集设备的 FPS、CPU、GPU、Ftrace 等指标数据&#xff1b; 目前 SmartPer…

uniapp之app版本更新,整体更新和热更新

目录 需求&#xff1a; 版本更新有两种更新模式&#xff1a; 实现&#xff1a; 前提&#xff1a; 热更新&#xff1a; 打包wgt包&#xff1a;菜单->发行->原生App-制作移动App资源升级包 代码逻辑: 整体更新&#xff1a; 实际项目开发&#xff1a; 需求&#xf…

Linux网络编程——C/C++Web服务器(二):IO多路复用select/poll/epoll实现服务器监听多客户端事件

环境配置&#xff1a;windows电脑用户可以安装WSL配置Linux环境&#xff0c;并且安装vscode及wsl的插件通过vscode连接本机电脑的Linux。 前置内容&#xff1a; Linux网络编程——C/CWeb服务器&#xff08;一&#xff09;&#xff1a;不断创建新线程处理多客户端连接和通信-C…

代码随想录算法训练营第二十七天(贪心 一)

硬拖拖到现在才写完。。。 关于贪心: 文章链接: 代码随想录 文章摘要: 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 贪心算法并没有固定的套路。 和其他算法不同&#xff0c;贪心没有能看出局部最优是否能推出整体最优的通法。 用来验证可不可以…

软件渗透测试必要性简析,第三方软件测试机构如何进行渗透测试?

在信息技术迅速发展的今天&#xff0c;软件渗透测试逐渐成为了确保信息安全的重要环节。软件渗透测试指的是对系统或应用程序进行模拟攻击&#xff0c;以发现其潜在的安全风险与脆弱性。不同于传统的安全审计&#xff0c;渗透测试更注重实际攻击过程和攻击者的视角&#xff0c;…

IO进程线程8月26ri

1&#xff0c;思维导图 2&#xff0c;用两个进程分别复制文件的上下两部分到另一个文件 #include<myhead.h> int main(int argc, const char *argv[]) {int fpopen("./1.txt",O_RDONLY);if(fp-1){perror("open");return -1;}int countlseek(fp,0,SE…

如何在 mind+ 中编写 python 程序

打开Mind&#xff0c;点击窗口右上角的【Python模式】按钮&#xff0c;由实时模式切换到Python模式。 将默认的循环执行模块拖动到左边的模块区删除。 点击【变量】&#xff0c;将【打印【Hello World】】模块拼接到【Python主程序开始】下方。 将【获取输入&#xff0c;提示语…

redis(未授权访问漏洞)

环境准备 下载并安装Redis 首先&#xff0c;下载Redis的源代码包并解压&#xff1a; wget http://download.redis.io/releases/redis-2.8.17.tar.gz tar xzf redis-2.8.17.tar.gz cd redis-2.8.17接着&#xff0c;编译安装Redis&#xff1a; 编译完成后&#xff0c;进入src目录…

自动化任务工具 | zTasker v1.97.1 绿色版

在自动化任务管理领域&#xff0c;一款名为zTasker的软件以其卓越的性能和易用性脱颖而出。今天&#xff0c;电脑天空将为大家详细介绍这款软件的亮点和使用场景。 功能特点 1. 轻量级设计&#xff0c;快速启动 zTasker以其小巧的体积和快速的启动速度&#xff0c;为用户提供…

模型 7S分析法(麦肯锡)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。组织全面诊断&#xff0c;战略协同优化。 1 7S分析法(麦肯锡)的应用 1.1 邮储银行的转型&#xff1a;基于麦肯锡7S模型的竞争力提升 中国邮储银行面临着激烈的金融行业竞争&#xff0c;为了迅速提升…

考研数学 高等数学----导数应用

核心框架 前置知识 正式内容 知识点1: 知识点2: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知…

嵌入式系统课后习题(带答案)

资料截图&#xff08;部分&#xff09;&#xff1a; &#x1f680; 获取更多详细资料可点击链接进群领取&#xff0c;谢谢支持&#x1f447; 点击免费领取更多资料

Ant Design Vue中Modal.confirm无法自动关闭

温馨tips:着急看解决方法可跳过碎碎念~ 前两天经理扔给我一个问题&#xff1a;“这个弹窗怎么关不上了&#xff1f;” 我怀着无所谓的心态&#xff1a;小意思啦&#xff0c;5分钟之内解决完~ …当然flag是不能随便乱立的 拉下来项目&#xff08;原神启动&#xff08;不是&…

@ohos.systemParameterEnhance系统参数接口调用:获取系统属性

在去年的文章&#xff0c;笔者介绍了如何使用修改修改OpenHarmony 设备厂家名称 、硬件版本号 等系统属性&#xff0c;本文介绍一下在应用层怎么获取系统属性。 开发环境 DAYU200 rk3568开发板OpenHarmony 4.1r API 10 (full sdk)DevEco Studio 4.1 Release 开发步骤 1.首先…