java每日一记 —— mybatis的动态数据源切换

news2024/11/18 5:34:27

动态数据源切换

  • 1.环境初始化
  • 2.切换数据源代码
  • 3.第二节代码的测试
  • 4.用注解的方式进行优化

此代码在jdk11上测试通过,SpringBoot版本为2.7.14

1.环境初始化

1.创建两个库

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- 表结构
DROP TABLE IF EXISTS `t_stu`;
CREATE TABLE `t_stu`  (
  `id` int NOT NULL COMMENT 'id',
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `age` int NULL DEFAULT NULL,
  `sex` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `class_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '课程名',
  `teacher` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '老师',
  `score` decimal(10, 2) NULL DEFAULT NULL COMMENT '分数',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

-- 库1数据
INSERT INTO `t_stu` VALUES (1, 'andy', 10, '男', '语文', '虚竹', 100.00);
-- 库2数据
INSERT INTO `t_stu` VALUES (1, 'lily', 10, '女', '数学', '小龙女', 100.00);

2.pom.xml

 <parent>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-parent</artifactId>
     <version>2.7.14</version>
     <relativePath/>
 </parent>
 <dependencies>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-rest</artifactId>
     </dependency>
     <dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>mybatis-plus-boot-starter</artifactId>
         <version>3.5.4</version>
     </dependency>
     <dependency>
         <groupId>com.alibaba</groupId>
         <artifactId>druid-spring-boot-starter</artifactId>
         <version>1.2.16</version>
     </dependency>
     <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>8.0.33</version>
     </dependency>
 </dependencies>

3.application.yml

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      db1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/andy_test_1?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false
        username: root
        password: root
      db2:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/andy_test_2?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false
        username: root
        password: root

4.pojo类

public class Stu implements Serializable {
    
    private Integer id;

    private String name;

    private Integer age;

    private String className;

    private BigDecimal score;

	// getter...
	// setter...
}

5.mapper

@Mapper
public interface StuMapper  extends BaseMapper<Stu> {

    List<Stu> findAll();

    int insertStu(@Param("stu") Stu stu);

}

6.mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.andy.mapper.StuMapper">

    <select id="findAll" resultType="com.andy.pojo.Stu">
        select * from t_stu
    </select>

    <insert id="insertStu" parameterType="com.andy.pojo.Stu">
        insert into t_stu(
        id,
        name)
        values(
        #{stu.id},
        #{stu.name})
    </insert>
</mapper>

7.controller

@RestController
public class TestController {
    @Resource
    private StuMapper stuMapper;

    @GetMapping("/getData/{datasourceName}")
    public String getMasterData(@PathVariable("datasourceName") String datasourceName){
        DataSourceAndyContextHolder.setDataSource(datasourceName);
        List<Stu> all = stuMapper.findAll();
        DataSourceAndyContextHolder.removeDataSource();
        return all.get(0).getName();
    }

    @GetMapping("/insertData/{datasourceName}")
    @Transactional  // spring的事务注解,在本案例中不会生效
    public void insertData(@PathVariable("datasourceName") String datasourceName, @RequestBody Stu stu){
        DataSourceAndyContextHolder.setDataSource(datasourceName);
        stuMapper.insertStu(stu);
        // int i = 3/0; 这一行是为了测试事务,各位大佬可以自行测似
        DataSourceAndyContextHolder.removeDataSource();
    }
}

2.切换数据源代码

1.先来个存储数据源的map

public class DataSourceAndyContextHolder {
    // 此类提供线程局部变量。这些变量不同于它们的正常对应关系是每个线程访问一个线程(通过get、set方法),有自己的独立初始化变量的副本。
    private static final ThreadLocal<String> DATASOURCE_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源
     * @param dataSourceName 数据源名称
     */
    public static void setDataSource(String dataSourceName){
        DATASOURCE_HOLDER.set(dataSourceName);
    }

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

    /**
     * 删除当前数据源
     */
    public static void removeDataSource(){
        DATASOURCE_HOLDER.remove();
    }

}

2.实现动态切换数据源

public class DynamicDataAndySource extends AbstractRoutingDataSource {
    public DynamicDataAndySource(DataSource defaultDataSource, Map<Object, Object> targetDataSources){
        super.setDefaultTargetDataSource(defaultDataSource);
        super.setTargetDataSources(targetDataSources);
    }

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

3.设置数据源

@Configuration
public class DateSourceAndyConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.druid.db1")
    public DataSource masterDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.db2")
    public DataSource slaveDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataAndySource createDynamicDataSource(){
        Map<Object,Object> dataSourceMap = new HashMap<>();
        DataSource defaultDataSource = masterDataSource();
        dataSourceMap.put("db1",defaultDataSource);
        dataSourceMap.put("db2",slaveDataSource());
        return new DynamicDataAndySource(defaultDataSource,dataSourceMap);
    }
}

3.第二节代码的测试

1.测试db1库

127.0.0.1:8080/getData/db1

在这里插入图片描述

2.测试db2库

127.0.0.1:8080/getData/db2

在这里插入图片描述

4.用注解的方式进行优化

1.导入坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.定义一个注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface AndyDS {
    String value() default "db1";
}

3.定义切面

@Aspect
@Component
@Slf4j
public class DSAspect {

    @Pointcut("@annotation(com.andy.config.AndyDS)")
    public void dynamicDataSource(){}

    @Around("dynamicDataSource()")
    public Object datasourceAround(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature)point.getSignature();
        Method method = signature.getMethod();
        AndyDS ds = method.getAnnotation(AndyDS.class);
        if (Objects.nonNull(ds)){
            DataSourceAndyContextHolder.setDataSource(ds.value());
        }
        try {
            return point.proceed();
        } finally {
            DataSourceAndyContextHolder.removeDataSource();
        }
    }
}

4.测试代码

    @GetMapping("/getData_2")
    @AndyDS("db2") 
    public List<Stu> getData_1(){
        List<Stu> all = stuMapper.findAll();
        return all;
    }

5.测试结果

127.0.0.1:8080/getData_2

在这里插入图片描述

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

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

相关文章

电脑录屏软件哪个好?全面对比,为您揭秘

电脑录屏软件成为创作者、教育者以及普通用户的得力工具&#xff0c;用于捕捉屏幕上的精彩瞬间或创作教学内容&#xff0c;可是电脑录屏软件哪个好呢&#xff1f;本文将深入比较三款备受瞩目的电脑录屏软件&#xff0c;通过详细的分步骤介绍&#xff0c;帮助用户了解它们的使用…

【Element-ui】Icon 图标与Button 按钮

文章目录 前言一、Icon 图标1.1 作用1.2 使用方法1.3 图标集合 二、Button 按钮2.1 基础用法2.2 禁用状态2.3 文字按钮2.4 图标按钮2.5 按钮组2.6 加载中2.7 不同尺寸 总结 前言 在前端开发中&#xff0c;界面的设计和交互是至关重要的一部分。一个直观、易用的界面往往离不开…

在winform中使用blazor hybrid构建页面

1、Blazor Hybrid简介 Blazor Hybrid 使开发人员能够将桌面和移动本机客户端框架与 .NET 和 Blazor 结合使用。在 Blazor Hybrid 应用中&#xff0c;Razor 组件在设备上是本机运行的。 这些组件通过本地互操作通道呈现到嵌入式 Web 视图控件。 组件不在浏览器中运行&#xff0…

怎么将用户引流到你的私域中?

微信私域运营是一种利用微信平台建立与用户深度联系的营销方式&#xff0c;可在私域中触达并服务用户。 那么如何将在将用户引流至你的私域中呢&#xff1f; 可以从以下几个小方法入手。 ①打造一个吸引人的个人品牌形象非常重要。在社交媒体上展示真实、独特、专业的一面&a…

oracle实验2023-12-8--触发器

第十四周实验 【例】功能要求&#xff1a;增加一新表XS_1&#xff0c;表结构和表XS相同&#xff0c;用来存放从XS表中删除的记录。 分析: 1、创建表 xs_1 SQL> create table xs_1 as select * from xs; Table created SQL> truncate table xs_1; Table truncated题目&a…

详解数据入表准备工作:如何判定数据资源?

务部发布的《企业数据资源相关会计处理暂行规定》即将于明年1月生效&#xff0c;但《暂行规定》并未详细解释数据资源这一概念。到底什么是数据资源&#xff0c;怎样将数据资源入表&#xff0c;成为众多国有企业与上市公司关心的问题。 — 01 — 什么是数据资源&#xff1f;…

【Linux系统化学习】命令行参数 | 环境变量的再次理解

个人主页点击直达&#xff1a;小白不是程序媛 Linux专栏&#xff1a;Linux系统化学习 代码仓库&#xff1a;Gitee 目录 mian函数传参获取环境变量 手动添加环境变量 导出环境变量 environ获取环境变量 本地变量和环境变量的区别 Linux的命令分类 常规命令 内建命令 …

PostgreSQL 技术内幕(十二) CloudberryDB 并行化查询之路

随着数据驱动的应用日益增多&#xff0c;数据查询和分析的量级和时效性要求也在不断提升&#xff0c;对数据库的查询性能提出了更高的要求。为了满足这一需求&#xff0c;数据库引擎不断经历创新&#xff0c;其中并行执行引擎是性能提升的重要手段之一&#xff0c;逐渐成为数据…

最新版IDEA专业版大学生申请免费许可证教学(无需学校教育邮箱+官方途径+非破解手段)

文章目录 前言1. 申请学籍在线验证报告2. 进入IDEA官网进行认证3. 申请 JB (IDEA) 账号4. 打开 IDEA 专业版总结 前言 当你进入本篇文章时, 你应该是已经遇到了 IDEA 社区版无法解决的问题, 或是想进一步体验 IDEA 专业版的强大. 本文是一篇学生申请IDEA免费许可证的教学, 在学…

SpringAMQP 快速入门

SpringAMQP 快速入门 1. 创建项目2. 快速入门2.2.1 消息发送2.2.2 消息接收 3. 交换机3.1 Fanout Exchange&#xff08;扇出交换机&#xff09;3.1.1 创建队列与交换机3.1.2 消息接收3.1.3 消息发送 3.2 Direct Exchange&#xff08;直连交换机&#xff09;3.2.1 创建交换机与队…

harmonyOS学习笔记之@Provide装饰器和@Consume装饰器

Provide和Consume&#xff0c;应用于与后代组件的双向数据同步&#xff0c;应用于状态数据在多个层级之间传递的场景。不同于State/Link装饰器修饰的 父子组件之间通过命名参数机制传递&#xff0c;Provide和Consume摆脱参数传递机制的束缚&#xff0c;实现跨层级传递。 其中Pr…

Edge 中的msedgewebview2总想联网

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 使用Edge浏览器的时候&#xff0c;右下角火绒总会弹出“msedgewebview2”想要联网的弹窗&#xff0c;如下 点击发起程序&#xff0c;找到路径如下&#xff1a; C:\Program Files (x86)\Microsoft\…

ChatGPT在国内的使用限制,国内的ChatGPT替代工具

人工智能技术的发展不仅改变了我们的生活方式&#xff0c;也在各行各业发挥着越来越重要的作用。ChatGPT&#xff08;Generative Pre-trained Transformer&#xff09;作为一种先进的自然语言处理模型&#xff0c;由OpenAI推出&#xff0c;其在生成人类般流畅对话方面表现出色。…

【USRP】5G / 6G OAI 系统 5g / 6G OAI system

面向5G/6G科研应用 USRP专门用于5G/6G产品的原型开发与验证。该系统可以在实验室搭建一个真实的5G 网络&#xff0c;基于开源的代码&#xff0c;专为科研用户设计。 软件无线电架构&#xff0c;构建真实5G移动通信系统 X410 采用了目前流行的异构式系统&#xff0c;融合了FP…

QT使用SQLite 超详细(增删改查、包括对大量数据快速存储和更新)

QTSQLite 在QT中使用sqlite数据库&#xff0c;有多种使用方法&#xff0c;在这里我只提供几种简单&#xff0c;代码简短的方法&#xff0c;包括一些特殊字符处理。在这里也给大家说明一下&#xff0c;如果你每次要存储的数据量很大&#xff0c;建议使用事务&#xff08;代码中…

dell服务器安装PERCCLI

因在linux 系统中无法查看系统磁盘的raid级别&#xff0c;也无法得知raid状态&#xff0c;需要安装额外的包来监控&#xff0c;因是dell服务器&#xff0c;就在dell网站中下载并安装 1、下载链接&#xff1a;驱动程序和下载 | Dell 中国https://www.dell.com/support/home/zh-…

一款Java实现的玩爆工具

这是一款涵盖娱乐到工作的软件程序&#xff0c;模块菜单包含&#xff1a;精选、博客园、观天下、听雨楼、短视频、电影、电视剧、藏金阁、云存储等诸多功能于一身的软件&#xff0c;下面我们来介绍一下软件的一些功能&#xff1a; 博客园&#xff1a;这是一个可以预览博客也可以…

论文笔记--Gemini: A Family of Highly Capable Multimodal Models

论文笔记-- 1. 文章简介2. 文章概括3 文章重点技术3.1 模型架构3.2 训练数据3.3 模型评估3.3.1 文本3.3.1.1 Science3.3.1.2 Model sizes3.3.1.3 Multilingual3.3.1.4 Long Context3.3.1.5 Human preference 3.3.2 多模态3.3.2.1 图像理解3.3.2.2 视频理解3.3.2.3 图像生成3.3.…

【mysql】隔离级别以及其验证实例

目录 前言 读未提交&#xff08;Read Uncommitted&#xff09; 读已提交&#xff08;Read Committed&#xff09; 可重复读&#xff08;Repeatable Read&#xff09; 串行化&#xff08;Serializable&#xff09; 前言 在事物与隔离级别中我们讲解了事务与不同隔离级别&a…

Word中在公式后面自动加入序号

1、在第一个公式后面输入&#xff08;英文输入法下&#xff09;#()&#xff0c;并且把光标放到两个括号之间&#xff1a; 2、点击插入-文档部件-域&#xff0c;类别选编号&#xff0c;域名选AutoNum&#xff0c;格式选1&#xff0c;2&#xff0c;3&#xff0c;…&#xff0c;确…