springboot+mybatisplus 动态切换数据源——数据库(通过AOP获取head参数动态切换)

2023-11-10

写这个博客,是记录一下自己的学习过程。
因为网上信息杂乱不清,所以此博客只做记录和分享作用。
参考博客链接:https://blog.csdn.net/QiuHaoqian/article/details/120479890
再此基础上修改了一些,使得mybatisplus无mapper.xml文件可以使用此代码。
先上效果图:
在这里插入图片描述

可以看出这个数据库没有这个帐号。
在这里插入图片描述
然后更换请求头Language参数,即可更换数据源-数据库
在这里插入图片描述
登录成功,返回数据。

实现,只需要写四个类,外加配置数据库和导入相关jar包
在这里插入图片描述

话不多说,先上pom.xml

      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid-spring-boot-starter</artifactId>
          <version>1.2.3</version>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>

然后配置文件 application.yml

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
spring:
  datasource:
    primary:
    //此处配置自己的第一个数据库
      jdbc-url: jdbc:mysql://localhost:3306/XXXX1?useSSL=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=Asia/Shanghai
      username: root
      password: XXXXX
      driver‐class‐name: com.mysql.cj.jdbc.Driver
    secondary:
    //此处配置自己的第二个数据库
      jdbc-url: jdbc:mysql://localhost:3306/XXXX2?useSSL=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=Asia/Shanghai
      username: root
      password: XXXXX
      driver‐class‐name: com.mysql.cj.jdbc.Driver
server:
  port:
    8081

再上相关类:

DataSourceAspect.java

package com.xxxx.aop;

import com.xxxx.datasource.DataSourceHolder;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Objects;


/**
 * 拦截controller方法,从请求头中获取使用的数据库编号
 *
 * @author qyz
 */
@Aspect
@Order(1)
@Configuration
public class DataSourceAspect {

    /**
     * 数据库名称在请求头中的key(从header中取)
     */
    private static final String LANGUAGE = "Language";

    /**
     * 切入点,放在controller的每个方法上进行切入,更新数据源
     */
     //这里填入自己项目的controller路径
    @Pointcut("execution(* com.1这里要改.controller..*.*(..))")
    private void anyMethod() {
    }

    @Before("anyMethod()")
    public void dataSourceChange() {
        // 请求头head中获取对应数据库编号 name=Language
        String Language = ((ServletRequestAttributes) Objects
                .requireNonNull(RequestContextHolder.getRequestAttributes()))
                .getRequest()
                .getHeader(LANGUAGE);
        System.out.println("当前数据源: " + Language);
        if (StringUtils.isBlank(Language)) {
            // TODO 根据业务抛异常
            throw new NullPointerException("请求头中没有" + LANGUAGE);
        }
        // 根据请求头中数据库名称来更改对应的数据源(核心)
        DataSourceHolder.setDataSource(Language);
    }

    @After("anyMethod()")
    public void after() {
        // 数据源重置(必须在请求完成后清空ThreadLocal线程上下文,否则会内存溢出)
        DataSourceHolder.clearDataSource();
    }
}

DataSourceHolder.java

package com.xxxx.datasource;

public class DataSourceHolder {

    /**
     * 线程本地环境 (存储数据库名称)
     */
    private static final ThreadLocal<String> DATA_SOURCES = new ThreadLocal<>();

    /**
     * 设置数据源(动态切换数据源),就是调用这个setDataSource方法
     */
    public static void setDataSource(String customerType) {
        DATA_SOURCES.set(customerType);
    }

    /**
     * 获取数据源
     */
    public static String getDataSource() {
        return DATA_SOURCES.get();
    }

    /**
     * 清除数据源
     */
    public static void clearDataSource() {
        DATA_SOURCES.remove();
    }
}

DynamicDataSource.java

package com.xxxx.datasource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 每次请求动态请求哪一个数据源
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceHolder.getDataSource();
    }
}

DynamicDataSourceConfig.java

package com.xxxx.datasource;

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;


@Configuration
//改成你项目的mapper路径
@MapperScan(basePackages = "com.2这里也要改.mapper", sqlSessionFactoryRef = "SqlSessionFactory")
public class DynamicDataSourceConfig {

    /**
     * 将第1个数据源对象放入Spring容器中
     *
     * @ConfigurationProperties 读取application.properties中的前缀为spring.datasource.primary的配置参数并映射成为一个对象
     */
    @Bean(name = "dateSource1")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource DateSource1() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 将第2个数据源对象放入Spring容器中
     */
    @Bean(name = "dateSource2")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource DateSource2() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 将动态代理数据源对象放入Spring容器中
     */
    @Bean(name = "dynamicDataSource")
    public DynamicDataSource DynamicDataSource(@Qualifier("dateSource1") DataSource primaryDataSource,
                                               @Qualifier("dateSource2") DataSource secondaryDataSource) {
        // 这个地方是比较核心的targetDataSource 集合是我们数据库和名字之间的映射
        Map<Object, Object> targetDataSource = new HashMap<>();
        targetDataSource.put("ch", primaryDataSource);
        targetDataSource.put("en", secondaryDataSource);

        DynamicDataSource dataSource = new DynamicDataSource();
        // 设置所有的数据源
        dataSource.setTargetDataSources(targetDataSource);
        // 设置默认使用的数据源对象
        dataSource.setDefaultTargetDataSource(primaryDataSource);

        return dataSource;
    }


    @Bean(name = "SqlSessionFactory")
    public SqlSessionFactory SqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicDataSource);
        //也是改成pojo路径(pojo实体类)
        sqlSessionFactoryBean.setTypeAliasesPackage("com.3这里要改.pojo");

        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        sqlSessionFactoryBean.setConfiguration(configuration);

        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }
}

基本的增删改查代码就不贴了。
测试如上方效果图在请求头填入参数即可。
至此项目完成。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

springboot+mybatisplus 动态切换数据源——数据库(通过AOP获取head参数动态切换) 的相关文章

  • 如何使用assertEquals 和 Epsilon 在 JUnit 中断言两个双精度数?

    不推荐使用双打的assertEquals 我发现应该使用带有Epsilon的形式 这是因为双打不可能100 严格 但无论如何我需要比较两个双打 预期结果和实际结果 但我不知道该怎么做 目前我的测试如下 Test public void te
  • 如何在 Spring 中禁用使用 @Component 注释创建 bean?

    我的项目中有一些用于重构逻辑的通用接口 它看起来大约是这样的 public interface RefactorAwareEntryPoint default boolean doRefactor if EventLogService wa
  • jQuery AJAX 调用 Java 方法

    使用 jQuery AJAX 我们可以调用特定的 JAVA 方法 例如从 Action 类 该 Java 方法返回的数据将用于填充一些 HTML 代码 请告诉我是否可以使用 jQuery 轻松完成此操作 就像在 DWR 中一样 此外 对于
  • 在 Jar 文件中运行 ANT build.xml 文件

    我需要使用存储在 jar 文件中的 build xml 文件运行 ANT 构建 该 jar 文件在类路径中可用 是否可以在不分解 jar 文件并将 build xml 保存到本地目录的情况下做到这一点 如果是的话我该怎么办呢 Update
  • 在接口中使用默认方法是否违反接口隔离原则?

    我正在学习 SOLID 原则 ISP 指出 客户端不应被迫依赖于他们所使用的接口 不使用 在接口中使用默认方法是否违反了这个原则 我见过类似的问题 但我在这里发布了一个示例 以便更清楚地了解我的示例是否违反了 ISP 假设我有这个例子 pu
  • 来自 dll 的 Java 调用函数

    我有这个 python 脚本导入zkemkeeperdll 并连接到考勤设备 ZKTeco 这是我正在使用的脚本 from win32com client import Dispatch zk Dispatch zkemkeeper ZKE
  • Java 公历日历更改时区

    我正在尝试设置 HOUR OF DAY 字段并更改 GregorianCalendar 日期对象的时区 GregorianCalendar date new GregorianCalendar TimeZone getTimeZone GM
  • MySQL 追加字符串

    How can I append a string to the end of an existing table value Let s say I have the table below And let s say that Mari
  • 没有 Spring 的自定义 Prometheus 指标

    我需要为 Web 应用程序提供自定义指标 问题是我不能使用 Spring 但我必须使用 jax rs 端点 要求非常简单 想象一下 您有一个包含键值对的映射 其中键是指标名称 值是一个简单的整数 它是一个计数器 代码会是这样的 public
  • 从Django中具有外键关系的两个表中检索数据? [复制]

    这个问题在这里已经有答案了 This is my models py file from django db import models class Author models Model first name models CharFie
  • 随机组合 MySQL 数据库中的两个单词

    我有一个包含名词和形容词的数据库 例如 id type word 1 noun apple 2 noun ball 3 adj clammy 4 noun keyboard 5 adj bloody ect 我想创建一个查询 它将抓取 10
  • volatile、final 和synchronized 安全发布的区别

    给定一个带有变量 x 的 A 类 变量 x 在类构造函数中设置 A x 77 我们想将 x 发布到其他线程 考虑以下 3 种变量 x 线程安全 发布的情况 1 x is final 2 x is volatile 3 x 设定为同步块 sy
  • 在我的 Spring Boot 示例中无法打开版本 3 中的 Swagger UI

    我在 Spring Boot 示例中打开 swagger ui 时遇到问题 当我访问 localhost 8080 swagger ui 或 localhost 8080 root api name swagger ui 时出现这种错误 S
  • 不接受任何内容也不返回任何内容的函数接口[重复]

    这个问题在这里已经有答案了 JDK中是否有一个标准的函数式接口 不接受也不返回任何内容 我找不到一个 像下面这样 FunctionalInterface interface Action void execute 可运行怎么样 Functi
  • 专门针对 JSP 的测试驱动开发

    在理解 TDD 到底是什么之前 我就已经开始编写测试驱动的代码了 在没有实现的情况下调用函数和类可以帮助我以更快 更有效的方式理解和构建我的应用程序 所以我非常习惯编写代码 gt 编译它 gt 看到它失败 gt 通过构建其实现来修复它的过程
  • Cucumber 0.4.3 (cuke4duke) 与 java + maven gem 问题

    我最近开始为 Cucumber 安装一个示例项目 并尝试使用 maven java 运行它 我遵循了这个指南 http www goodercode com wp using cucumber tests with maven and ja
  • 使用反射覆盖最终静态字段是否有限制?

    在我的一些单元测试中 我在最终静态字段上的反射中遇到了奇怪的行为 下面是说明我的问题的示例 我有一个基本的 Singleton 类 其中包含一个 Integer public class BasicHolder private static
  • 创建一个 JSON 对象以在 Spring Boot 测试中发布

    我想编写基本测试来使用 JSON 负载在 users URL 上执行 POST 请求来创建用户 我找不到如何将新对象转换为 JSON 到目前为止有这么多 这显然是错误的 但解释了目的 Test public void createUser
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • Spring Boot 无法更新 azure cosmos db(MongoDb) 上的分片集合

    我的数据库中存在一个集合 documentDev 其分片键为 dNumber 样本文件 id 12831221wadaee23 dNumber 115 processed false 如果我尝试使用以下命令通过任何查询工具更新此文档 db

随机推荐

  • 2021最新Java笔试题及答案,Java面试真题精选

    二 回顾整理阿里面试题 基本就这样了 还有一些零星的问题想不起来了 答案也整理出来了 自我介绍 JVM如何加载一个类的过程 双亲委派模型中有哪些方法 HashMap如何实现的 HashMap和ConcurrentHashMap区别 Conc
  • 【安全开发】python—基于正则表达式的爬虫

    0x00 前置 1 搜索引擎 百度 谷歌 企业内部知识库 2 互联网 公网 无需授权 深网 需要授权 暗网 非正式渠道 无法使用常规手段访问 友情提醒 没事别看 3 规则 爬取互联网公开的信息 但正常情况也需要遵守robots协议 网络爬虫
  • 【华为OD机试 2023 B卷

    在线OJ 已购买本专栏用户 请私信博主开通账号 在线刷题 运行出现 Runtime Error 0Aborted 请忽略 华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一
  • Qt Graphics View Framework 图形视图框架

    QT的2D绘图我们使用QPainter实现少量绘图是很理想的 但是如果是要绘制大量的2D图形并且需要能够控制管理时就会显得力不从心 好在QT视图体系提供了QGraphics View Framework的框架 使用这个框架可以将各图形项进行
  • MySQL 8.0用户管理

    1 禁止root用户远程访问 1 在安装MySQL数据库软件时 我们已经设置了禁止root远程登录 参见 https blog csdn net chengyuqiang article details 121282247 2 root用户
  • Kubernetes (k8s 1.23) 安装与卸载

    镜像下载 域名解析 时间同步请点击 阿里云开源镜像站 请注意k8s在1 24版本不支持docker容器 本文使用kubeadm进行搭建 1 查看系统版本信息以及修改配置信息 1 1 安装k8s时 临时关闭swap 如果不关闭在执行kubea
  • MySQL的sum函数优化

    有一张表 结构如下 Field Type Null Key Default Extra ID bigint 20 NO PRI NULL auto increment
  • 51单片机开发问题之不能直接对端口管脚输出高低点电平

    在做51单片机点亮一个了led的时候遇到一个问题 不能直接用P0 0 0 对单片机管脚输出高低电平 Keil编译器会报错 如图 用sbit定义一个变量来控制管脚就行
  • 网络安全学习路线是怎样的?从入门到入坟,看这篇就够了

    为什么要学习安全 近几年网络安全事件层出不穷 相信大家哪怕没遇到过 但也听过不少了吧 如服务器遭受入侵被黑 用户帐号被盗 被钓鱼 勒索病毒等 这些一旦发生 对企业而言都是不小的打击 因此 网络安全不容忽视 同时随着时代的不断发展 公司对于运
  • DOM0和DOM2

    DOM事件绑定 DOM0级事件 特点 所有的浏览器都支持 事件只能注册一次 后面的会覆盖旧的 注册方式 1 在html上添加 div div 其中this指向自身 2 匿名函数d nclick function 其中 this 指向自身 3
  • 美国科技大佬成功之前混得都有多惨

    摘要 超过三分之一在美国成立的顶尖高科技公司是由在国外出生的人创立的 他们的成功经验带动许多移民来到美国实现美国梦的希望 id cproIframe u1503021 2 width 250 height 250 src http pos
  • 使用DBN实现风速预测matlab代码

    使用DBN实现风速预测matlab代码 风速预测在实际生产生活中有着广泛的应用 对于风电场 农业灌溉 城市规划等领域都有着重要的意义 本文将介绍如何使用深度信念网络 Deep Belief Networks DBN 来实现风速预测 并提供相
  • Mysql数据库的导入和导出

    Mysql数据库的导入和导出 1 导入 2 导出 Mysql数据库的可移植性是比较好的 在命令行状态下可用仅仅一行的代码就实现数据库的导入和导出 不论是在Windows操作系统还是Linux操作系统下的命令行 命令基本上完全一致 1 导入
  • QT主窗口与子窗口互相传值笔记

    1 主窗口向子窗口传值 将子窗口定义为主窗口的成员变量 在子窗口中定义接受数据的成员变量 主窗口中可以使用子窗口成员变量来访问他的成员变量 于是就能直接赋值了 void Main wgt test son wgt num 18 2 子窗口向
  • PAT 1015 德才论 (25分) C语言实现

    1015 德才论 25分 宋代史学家司马光在 资治通鉴 中有一段著名的 德才论 是故才德全尽谓之圣人 才德兼亡谓之愚人 德胜才谓之君子 才胜德谓之小人 凡取人之术 苟不得圣人 君子而与之 与其得小人 不若得愚人 现给出一批考生的德才分数 请
  • 将文件或目录移动到另外的目录

    File or directory to be moved File file new File filename Destination directory File dir new File directoryname Move fil
  • Flutter编译卡在Running Gradle task ‘assembleDebug‘

    为什么卡在这一步 把网络断开 发现了问题 卡在了下载运行环境和依赖库的步骤 jcenter 下载速度太慢 知道原因就好办了 需要修改3个地方 1 首先找到fultter sdk所在文件夹的位置 依次进入 flutter packages f
  • 单端口RAM实现FIFO

    RAM分类 单口ram 单端口RAM只有一组数据线和一组地址线 只有一个时钟 读写共用地址线 输出只有一个端口 所以单端口RAM的读写操作不能同时进行 当wea拉高时 会将数据写入对应的地址 同时douta输出的数据与此时写入的数据是一致的
  • STC32G12K128内部集成的I2C总线实现oled12864显示

    STC32G 系列的单片机内部集成了一个 I2C 串行总线控制器 I2C 是一种高速同步通讯总线 通讯使用 SCL 时钟线 和 SDA 数据线 两线进行同步通讯 对于 SCL 和 SDA 的端口分 STC32G 系列的单片机提供了切换模式
  • springboot+mybatisplus 动态切换数据源——数据库(通过AOP获取head参数动态切换)

    写这个博客 是记录一下自己的学习过程 因为网上信息杂乱不清 所以此博客只做记录和分享作用 参考博客链接 https blog csdn net QiuHaoqian article details 120479890 再此基础上修改了一些