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

news/2024/4/25 18:49:15

文章目录

  • 背景
  • 多数据源准备工作
    • 整体思路
  • 多数据源切换方式
    • 准备工作
    • 自动切换(@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 #设置默认的数据源或者数据源组,默认值即为masterstrict: 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%2B8username: rootpassword: 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%2B8username: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver

切换数据源代码

@RestController
@RequestMapping("/eee")
public class DataSourceTest {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate 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: closestat-view-servlet:enabled: truelogin-username: adminlogin-password: 123456dynamic:primary: master# /p6spy: truelazy: true# 配置全局druid参数,请按需配置druid:initial-size: 5max-active: 20min-idle: 5max-wait: 60000datasource:master:username: rootpassword: 123456url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8driver-class-name: com.mysql.cj.jdbc.Driver

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

@PostConstructpublic 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切换适用于频繁切换数据源、需要自动化调度的场景,而手动切换则更加灵活,适用于需要根据实际情况即时切换数据源的情况。通过合理的规划和技术实施,我们可以充分发挥各种切换方式的优势,实现数据源的灵活管理和高效切换,为业务运行提供有力支持。


https://www.xjx100.cn/news/3091580.html

相关文章

pytorch下载离线包的网址

下载地址&#xff1a;https://download.pytorch.org/whl/torch_stable.html 安装GPU版本需要安装&#xff1a;torch、torchvision、 注意版本需要对应上 格式&#xff1a;适用cuda版本&#xff0c;torch版本 或者 orchvision版本&#xff0c;cp38就是适用python 3.8版本 下…

FL Studio21怎么破解?2024年最新FLStudio21.2.0安装解锁特别版下载使用图文教程

用FL Studio编曲&#xff0c;让音乐成为你的翅膀&#xff0c;飞翔在无尽的创作海洋中吧&#xff01; FL Studio作为一款功能强大且备受赞誉的音乐制作软件&#xff0c;为你提供了一个独特的创作平台。通过FL Studio&#xff0c;你可以自由地创作、编曲&#xff0c;制作属于自己…

一篇文章让你彻底了解Java算法「十大经典排序算法」

✍️作者简介&#xff1a;码农小北&#xff08;专注于Android、Web、TCP/IP等技术方向&#xff09; &#x1f433;博客主页&#xff1a; 开源中国、稀土掘金、51cto博客、博客园、知乎、简书、慕课网、CSDN &#x1f514;如果文章对您有一定的帮助请&#x1f449;关注✨、点赞&…

腾讯云轻量应用服务器三年租用价格表_免去续费困扰

腾讯云服务器续费贵所以一次性买3年或5年&#xff0c;腾讯云轻量应用服务器3年价格有优惠&#xff0c;CVM云服务器5年有特价&#xff0c;腾讯云3年轻量和5年云服务器CVM优惠活动入口&#xff0c;3年轻量应用服务器配置可选2核2G4M和2核4G5M带宽&#xff0c;5年CVM云服务器可以选…

C#中的is和as的使用和区别

目录 概述一、is操作符1. is操作符的语法2. is操作符的用途3. is操作符的使用示例4. is操作符与typeof操作符的区别 二、as操作符1. as操作符的语法2. as操作符的用途3. as操作符的使用示例4. as操作符与is操作符的区别和联系5. as操作符与is操作符的区别总结 概述 在C#编程语…

使用 gpg 对Linux下的文件加密

其实蛮简单的&#xff0c;Linux原生就有gpg命令。 gpg表示GNU Privacy Guard。PGP表示Pretty Good Privacy。有点绕&#xff0c;别搞混。 gpg 是 GNU Privacy Guard (GnuPG) 的 OpenPGP&#xff08;Pretty Good Privacy&#xff09;部分。 它是一个使用 OpenPGP 标准提供数字…

vue3使用pinia实现数据缓存

文章目录 前言一、pinia是什么&#xff1f;二、安装pinia三、注册pinia四、使用pinia定义数据及方法使用 优化如有启发&#xff0c;可点赞收藏哟~ 前言 vue2以前一直使用vuex实现状态管理 vue3之后推出了pinia… 一、pinia是什么&#xff1f; 直观、类型安全、轻便灵活的Vue …

第1天:Python基础语法(四)

正文&#xff1a; 在之前的文章中&#xff0c;我们已经学习了Python的基本语法关于列表的一些常用操作。 在本篇文章中&#xff0c;我们将继续学习其他类型 字典类型 ➢ 在python中&#xff0c;dict用来代表字典&#xff0c;并且可以创建一个字典 ➢ 在python中&#xff0c…