【项目实战】多租户实现数据库动态切换

news2024/9/21 22:18:30

文章目录

  • 背景
  • 多数据源准备工作
    • 整体思路
  • 多数据源切换方式
    • 准备工作
    • 自动切换(@DS方式)
      • 配置文件设置
    • 手动切换
      • 配置文件设置
      • 项目启动加载数据源:使用注解@PostConstruct
      • 添加数据源
  • 总结

背景

最近公司项目中需要做多租户进行数据源切换的业务,目的是要达到租户与租户之间的数据要进行隔离,本次实现的实现是在一台服务器中,去进行多个数据库之间的切换,且不能相互影响。有不明白多租户的相关内容,可以查看资料:
多租户相关内容

多数据源准备工作

参考资料:https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611

整体思路

在请求发出的时候就去进行拦截(判断)这个用户所在的数据源,然后再去切换到对应的数据源中。
1、在我们的业务中,用户可以自己注册,然后就会生产一个数据库,那么在新用户进行登录的时候就要去实时的切换数据源。
2、那么对于已有的数据源,在项目启动的时候就去把所有的数据源加载到内存中。

在这里插入图片描述

多数据源切换方式

在数据源切换中,找到有两种切换方式一个自动,另外一个是手动切换。

准备工作

添加依赖:使用最新版本

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
  <version>${version}</version>
</dependency>

自动切换(@DS方式)

配置文件设置

自动切换也就是在配置文件中把需要用到的数据源准备好,这样就可以在使用的时候在注解上进行使用并进行切换。

spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master: # 数据源名称
          url: jdbc:mysql://116.204.118.226:3306/test1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
          username: root
          password: guyanshuang.
          driver-class-name: com.mysql.cj.jdbc.Driver
# 如下,如果你是确定的几个数据源,可以直接都在yaml配置写死即可
        slave_1:
          url: jdbc:mysql://116.204.118.226:3306/test2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver

切换数据源代码

@RestController
@RequestMapping("/eee")
public class DataSourceTest {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private DataSource dataSource;

    private DataSourceCreator dataSourceCreator;
    @DS("master")
    @GetMapping("/selectAll")
    public List selectAll() {
        return  jdbcTemplate.queryForList("select * from test");
    }

    @GetMapping("/selectByCondition")
    @DS("slave_1")
    public List selectByCondition() {
        return  jdbcTemplate.queryForList("select * from test");
    }

这里有两个方法,都使用@DS注解,但是他们的参数是不同的,这里也就是去切换到对应的数据源了。
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。

手动切换

配置文件设置

这里是使用了druid的配置

# 数据源配置
spring:
  datasource:
    druid:
      destroy-method: close
      stat-view-servlet:
        enabled: true
        login-username: admin
        login-password: 123456
    dynamic:
      primary: master
      # /p6spy: true
      lazy: true
      # 配置全局druid参数,请按需配置
      druid:
        initial-size: 5
        max-active: 20
        min-idle: 5
        max-wait: 60000
      datasource:
        master:
          username: root
          password: 123456
          url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
          driver-class-name: com.mysql.cj.jdbc.Driver

项目启动加载数据源:使用注解@PostConstruct

@PostConstruct
    public void initDataSource(){
        //查询所有的资源
       List<TanentModel> tanentModels =tantentMapper.queryAllDataSource();

        for (TanentModel tantent:tanentModels) {
            DataSourceProperty dataSourceProperty = new DataSourceProperty();
            dataSourceProperty.setUrl(tantent.getTenantUrl());
            dataSourceProperty.setDriverClassName(tantent.getTenantDriverClassName());
            dataSourceProperty.setUsername(tantent.getTenantUserName());
            dataSourceProperty.setPassword(tantent.getTenantUserPassword());
            dataSourceProperty.setPoolName(tantent.getTenantCode());
            //创建数据源
            javax.sql.DataSource createDataSource = dataSourceCreator.createDataSource(dataSourceProperty);
            //数据源添加到资源池中
            ((DynamicRoutingDataSource)dataSource).addDataSource(dataSourceProperty.getPoolName(),createDataSource);

        }

    }

添加数据源

在这里插入代码片 public void switchDataSource(String phone) {
        TanentModel tanentModel=null;//获取到的数据源
        try {
            if (!StringUtils.isEmpty(phone)){
                tanentModel= tantentMapper.queryDataSource(phone);

                DataSourceProperty dataSourceProperty = new DataSourceProperty();
                dataSourceProperty.setUrl(tanentModel.getTenantUrl());
                dataSourceProperty.setDriverClassName(tanentModel.getTenantDriverClassName());
                dataSourceProperty.setUsername(tanentModel.getTenantUserName());
                dataSourceProperty.setPassword(tanentModel.getTenantUserPassword());
                dataSourceProperty.setPoolName(tanentModel.getTenantCode());
                String poolName=tanentModel.getTenantCode();
                DynamicDataSourceContextHolder.clear();
                //切换到对应poolName的数据源
                // 判断数据源是否存在,不存在则添加
                javax.sql.DataSource createDataSource = dataSourceCreator.createDataSource(dataSourceProperty);
                //根据手机号查到的资源名称,获取资源池中的资源
                DataSource dataSourcePool = ((DynamicRoutingDataSource) this.dataSource).getDataSources().get(poolName);
                //如果资源为空,说明资源池中没有改资源,那么就添加到资源池中
                if (dataSourcePool==null){
                    //动态添加到资源池
                    ((DynamicRoutingDataSource)dataSource).addDataSource(dataSourceProperty.getPoolName(),createDataSource);
                }
                DynamicDataSourceContextHolder.push(poolName);
                String peek = DynamicDataSourceContextHolder.peek();
                System.out.println("当前线程数据源:"+peek);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

添加数据源核心代码:

  DynamicDataSourceContextHolder.push(poolName);

总结

多数据源切换是现代应用开发中的重要功能之一。对于数据源切换,我们可以采取两种方式:@DS切换和手动切换。

@DS切换是一种自动化方式,通过在代码中配置切换规则,系统可以根据预设条件自动切换数据源。这种方式能够减少人工操作的繁琐,提高效率和准确性。它适用于在特定场景下需要频繁切换数据源的情况,例如负载均衡、读写分离等。通过@DS切换,我们可以灵活地管理和调度多个数据源,实现资源的优化利用和负载均衡,从而提升系统的性能和可扩展性。

另一方面,手动切换是一种更加灵活的方式。它允许开发人员根据实际情况进行即时切换,通过编码的方式直接指定使用哪个数据源。这种方式适用于需要根据具体需求灵活切换数据源的场景,例如特定业务逻辑需要使用不同的数据源等。手动切换虽然需要人工介入,但它提供了更大的灵活性和可控性,使开发人员可以根据需要自由切换数据源,满足不同的业务需求。

综上所述,针对多数据源切换,我们可以根据具体业务需求和系统特点选择合适的切换方式。@DS切换适用于频繁切换数据源、需要自动化调度的场景,而手动切换则更加灵活,适用于需要根据实际情况即时切换数据源的情况。通过合理的规划和技术实施,我们可以充分发挥各种切换方式的优势,实现数据源的灵活管理和高效切换,为业务运行提供有力支持。

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

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

相关文章

Vue2问题:分享一个通用多文件类型预览库

前端功能问题系列文章&#xff0c;点击上方合集↑ 序言 大家好&#xff0c;我是大澈&#xff01; 本文约2000字&#xff0c;整篇阅读大约需要3分钟。 本文主要内容分三部分&#xff0c;第一部分是需求分析&#xff0c;第二部分是实现步骤&#xff0c;第三部分是问题详解。 …

UI自动化测试神器Playwright(Java版)(保存登录cookie,解决免登录)

&#x1f3ad; Playwright在称为浏览器上下文的隔离环境中执行测试。该隔离模型提高了可重复性并防止相关联的测试脚本执行失败。测试可以加载现有的已验证状态&#xff0c;比如获取已登录的状态&#xff08;Cookie&#xff09;&#xff0c;在后续脚本中复用。这消除了在每个测…

金蝶 EAS及EAS Cloud任意文件上传漏洞复现

0x01 产品简介 金蝶EAS Cloud为集团型企业提供功能全面、性能稳定、扩展性强的数字化平台&#xff0c;帮助企业链接外部产业链上下游&#xff0c;实现信息共享、风险共担&#xff0c;优化生态圈资源配置&#xff0c;构筑产业生态的护城河&#xff0c;同时打通企业内部价值链的数…

SQL 中的 NULL 值:定义、测试和处理空数据,以及 SQL UPDATE 语句的使用

SQL NULL 值 什么是 NULL 值&#xff1f; NULL 值是指字段没有值的情况。如果表中的字段是可选的&#xff0c;那么可以插入新记录或更新记录而不向该字段添加值。此时&#xff0c;该字段将保存为 NULL 值。需要注意的是&#xff0c;NULL 值与零值或包含空格的字段不同。具有 …

MR混合现实教学系统在汽车检修与维护课堂教学中的应用

传统的汽车检修与维护课堂教学主要依赖教师口头讲解和黑板演示&#xff0c;这种方式存在一定的局限性。首先&#xff0c;对于一些复杂的机械结构和操作过程&#xff0c;教师难以生动形象地展示给学生。其次&#xff0c;学生无法直接观察到实际操作中的细节和注意事项&#xff0…

WPF Button点击鼠标左键弹出菜单

目录 ContextMenu介绍WPF实现点击鼠标左键弹出菜单如何禁用右键菜单如何修改菜单样式菜单位置设置 本篇博客介绍WPF点击按钮弹出菜单&#xff0c;效果如下&#xff1a; 菜单的位置、央视可以自定义。 实现技巧&#xff1a;不在xaml里菜单&#xff0c;在按钮左键按下的点击事件里…

锚点优化步步为赢:详细解析关键知识点和最佳实践,提升网页的关联性与权威性!

为所有文本链接使用描述性锚文本。大多数搜索引擎在对网页进行排名时都会考虑输入链接的锚文本。下面是一个锚文本示例&#xff1a; <a href"otherpage.htm" title"Anchor Title">Anchor Text</a>下面列出了一些关于锚点的注意事项&#xff1…

LabVIEW如何获取波形图上游标所在位置的数值

LabVIEW如何获取波形图上游标所在位置的数值 获取游标所在位置数值的一种方法是利用波形图的游标列表属性。 在VI的程序框图中&#xff0c;右键单击波形图并选择创建引用 &#xff0c;然后将创建的引用节点放在程序框图上。 在程序框图上放置一个属性节点&#xff0c;并将其…

C++类与对象(3)—拷贝构造函数运算符重载

目录 一、拷贝构造函数 1、定义 2、特征 3、内置与自定义类型 4、const修饰参数 5、默认生成 浅拷贝 深拷贝 6、总结 二、运算符重载 1、定义 2、判断是否相等 3、比较大小 4、赋值 5、总结 一、拷贝构造函数 1、定义 拷贝构造函数&#xff1a;只有单个形参…

多目标应用:基于非支配排序的蜣螂优化算法NSDBO求解微电网多目标优化调度(MATLAB)

一、微网系统运行优化模型 微电网优化模型介绍&#xff1a; 微电网多目标优化调度模型简介_IT猿手的博客-CSDN博客 二、基于非支配排序的蜣螂优化算法NSDBO 基于非支配排序的蜣螂优化算法NSDBO简介&#xff1a; https://blog.csdn.net/weixin46204734/article/details/128…

如何挖掘xss漏洞

如何挖掘xss漏洞 对于如何去挖掘一个xss漏洞我是这样理解的 在实战情况下不能一上来就使用xss语句来进行测试很容易被发现 那这种情况该怎么办呢 打开准备渗透测试的web网站&#xff0c;寻找可以收集用户输入的地方比如搜索框&#xff0c;url框等 发现后寻找注入点 选在输入…

指令系统、流水线

指令系统 分类 寻址方式 设计 能够改变控制流的指令&#xff1a;分支、跳转、过程调用、过程返回 操作码设计 MIPS 流水线 MIPS流水线 改进后 取指&#xff08;IF&#xff09; 译码&#xff08;ID&#xff09; 执行&#xff08;EX&#xff09; 存储器访问 寄存器-寄存器 A…

【Kettle实战】字符串处理及网络请求JSON格式处理

经过大量的kettle操作实践&#xff0c;我们会渐渐掌握一些技巧&#xff0c;大大减轻清洗的工作量。比如在哪里 处理字符串更方便&#xff0c;在哪儿处理更合理都是一个取舍问题。 字符串拼接 MySQL中使用concat(字段1,字段2)&#xff0c;但是如果“字段2”为NULL&#xff0c;结…

基于nbiot的矿车追踪定位系统(论文+源码)

1.系统设计 鉴于智能物联网的大趋势&#xff0c;本次基于窄带物联网的矿车追踪定位系统应具备以下功能&#xff1a; &#xff08;1&#xff09;实现实时定位&#xff0c;真正实现矿车随时随地定位; &#xff08;2&#xff09;定位精度高&#xff0c;采用该系统可以实现矿车在…

Linux内核mmap内存映射详解及例子实现

mmap在linux哪里&#xff1f; 什么是mmap&#xff1f; 上图说了&#xff0c;mmap是操作这些设备的一种方法&#xff0c;所谓操作设备&#xff0c;比如IO端口&#xff08;点亮一个LED&#xff09;、LCD控制器、磁盘控制器&#xff0c;实际上就是往设备的物理地址读写数据。 但…

单链表的实现(Single Linked List)---直接拿下!

单链表的实现&#xff08;Single Linked List&#xff09;—直接拿下&#xff01; 文章目录 单链表的实现&#xff08;Single Linked List&#xff09;---直接拿下&#xff01;一、单链表的模型二、代码实现&#xff0c;接口函数实现①初始化②打印链表③创建一个结点④尾插⑤尾…

基于yolov8的车牌检测训练全流程

YOLOv8 是Ultralytics的YOLO的最新版本。作为一种前沿、最先进(SOTA)的模型&#xff0c;YOLOv8在之前版本的成功基础上引入了新功能和改进&#xff0c;以提高性能、灵活性和效率。YOLOv8支持全范围的视觉AI任务&#xff0c;包括检测, 分割, 姿态估计, 跟踪, 和分类。这种多功能…

使用element-plus 完成密码再次验证(修改密码)

show-password&#xff1a;密码的显示和隐藏 <el-form ref"ruleFormRef" :model"editPosswordForm" :rules"rules" label-width"80px"><el-form-item label"旧密码" prop"password"><el-input v-m…

echarts的使用

1. 普通版 其实主要就是option1&#xff0c;option1就是画的图 echats不能响应刷新&#xff0c;要想实时刷新监听刷新的值重新调用一下方法即可 html <div class"echart" style"width: 100%;height: calc(100% - 130px)" ref"main1">&l…

数据库存储引擎

一、MySQL体系结构 二、存储引擎-简介 存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的&#xff0c;而不是基于库的&#xff0c;所以存储引擎也可以被成为表的类型 MySQL 5.5版本之后&#xff0c;默认存储引擎就是InnoDB&#xff0c;之前…