[Mybatis-plus 框架应用 多租户数据隔离?]

news/2024/9/12 18:18:15/

目录

 ☕前言:

☕Mybatis-Plus适用于以下开发场景:

☕Mybatis-Plus提供了一些常用的增删改查函数,例如:

☕Mybatis-plus多租户 数据隔离示例:

☕在Mybatis-Plus的配置文件中增加多租户的配置,例如:

☕我们需要自定义一个租户处理器,这个处理器需要根据当前的租户ID设置数据源,例如:

☕我们需要定义多个数据源,每个租户对应一个数据源,例如:

☕我们需要在动态数据源中添加多个数据源,根据租户ID选择对应的数据源,例如:

☕☕使用Mybatis-Plus进行数据操作时,需要在Mapper接口中添加@TableName注解和@TableField注解,例如:

☕在进行数据操作时,我们需要设置当前线程的租户ID,例如:

☕我们需要实现租户ID的动态传递,例如在Web请求中,从请求头或者参数中获取租户ID,然后将租户ID保存到当前线程中,例如:

☕上面的代码实现了Mybatis-Plus的多租户数据隔离功能,其实现逻辑如下:

☕小伙伴肯定很疑问如何实现的租户隔离,具体在那一步?

综上所述,上面的代码中租户隔离是在使用Mybatis-Plus进行数据操作时实现的,通过在SQL语句中加入租户ID条件,以实现数据隔离。

☕Mybatis-Plus部分函数示例:

☕Mybatis-Plus提供了分页函数,可以方便地进行分页查询,例如:

☕Mybatis-Plus提供了条件构造器,可以方便地构造查询条件,例如:

☕Mybatis-Plus提供了使用Lambda表达式进行查询的方式,可以更加方便地构造查询条件,例如:

☕数据库主键生成策略:支持多数据方式以 native 总结组合生成、可单类里直接 使用 @Options 注解,借助增品并发的生成编号办法很舒服, 


 ☕前言:

    之前也是经常接触使用这个框架,想着沉淀一下内容吧,这里稍微讲解了一下多租户数据隔离,其他的就是一些函数介绍和使用.....

☕Mybatis-Plus适用于以下开发场景:

  • 快速开发:Mybatis-Plus提供了许多常用的持久化操作函数,可以快速进行开发,减少重复代码的编写。

  • 代码生成:Mybatis-Plus提供了代码生成器,可以根据数据库表自动生成实体类、Mapper接口、XML文件等,减少手动编写代码的工作量。

  • 多租户:Mybatis-Plus提供了多租户的支持,可以根据不同的租户进行数据隔离。

  • 分页查询:Mybatis-Plus提供了分页查询的支持,可以方便地进行分页查询操作。

  • 乐观锁:Mybatis-Plus提供了乐观锁的支持,可以避免并发更新时的数据冲突。

  • 数据库操作:Mybatis-Plus提供了许多数据库操作的支持,如批量插入、批量更新、批量删除等。

  • Lambda表达式:Mybatis-Plus提供了Lambda表达式的支持,可以使用Lambda表达式进行查询,代码更加简洁易懂。

☕Mybatis-Plus提供了一些常用的增删改查函数,例如:

  • insert():插入一条记录
  • insertBatch():批量插入记录
  • updateById():根据ID更新记录
  • update():根据条件更新记录
  • deleteById():根据ID删除记录
  • delete():根据条件删除记录
  • selectById():根据ID查询记录
  • selectBatchIds():根据ID列表查询记录
  • selectByMap():根据Map中的条件查询记录
  • selectOne():根据条件查询单条记录
  • selectList():根据条件查询多条记录

这些函数都可以在BaseMapper中找到。

☕Mybatis-plus多租户 数据隔离示例:

  • Mybatis-Plus提供了多租户的支持,可以根据不同的租户进行数据隔离。下面是一个简单的demo,假设我们有一个租户表和一个用户表,用户表中存储了租户的ID,我们需要实现多租户的数据隔离。

☕在Mybatis-Plus的配置文件中增加多租户的配置,例如:

其中,logic-not-delete-value表示逻辑删除的默认值,tenant-column为租户ID字段名,tenant-handler为租户处理器

mybatis-plus:global-config:db-config:# 多租户配置,设置租户ID的字段名logic-not-delete-value: 1tenant-column: tenant_idtenant-handler: com.example.handler.MyTenantHandler

☕我们需要自定义一个租户处理器,这个处理器需要根据当前的租户ID设置数据源,例如:

getTenantId方法返回当前线程中保存的租户ID,getTenantIdColumn返回租户ID字段名,doTableFilter方法可以过滤掉一些不需要进行租户隔离的表。

public class MyTenantHandler implements TenantHandler {@Overridepublic Expression getTenantId() {// 从当前线程中获取租户IDString tenantId = TenantContextHolder.getTenantId();// 返回租户ID的值return new LongValue(tenantId);}@Overridepublic String getTenantIdColumn() {// 返回租户ID的字段名return "tenant_id";}@Overridepublic boolean doTableFilter(String tableName) {// 过滤掉一些不需要进行租户隔离的表return !"user".equalsIgnoreCase(tableName);}
}

☕我们需要定义多个数据源,每个租户对应一个数据源,例如:

我们创建了两个数据源,分别对应两个租户。

@Configuration
public class DataSourceConfig {@Beanpublic DataSource dataSource1() {// 创建数据源1return new DruidDataSource();}@Beanpublic DataSource dataSource2() {// 创建数据源2return new DruidDataSource();}
}

☕我们需要在动态数据源中添加多个数据源,根据租户ID选择对应的数据源,例如:

我们创建了一个DynamicDataSource,并在其中添加了两个数据源,根据租户ID选择对应的数据源。

@Configuration
public class DynamicDataSourceConfig {@Autowiredprivate DataSource dataSource1;@Autowiredprivate DataSource dataSource2;@Bean@Primarypublic DataSource dynamicDataSource() {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put("1", dataSource1); // 租户1对应数据源1targetDataSources.put("2", dataSource2); // 租户2对应数据源2DynamicDataSource dynamicDataSource = new DynamicDataSource();dynamicDataSource.setTargetDataSources(targetDataSources);return dynamicDataSource;}
}

☕☕使用Mybatis-Plus进行数据操作时,需要在Mapper接口中添加@TableName注解和@TableField注解,例如:

@TableName("user")
public interface UserMapper extends BaseMapper<User> {@TableField("tenant_id")Integer getTenantId();@Select("SELECT id, name FROM user WHERE tenant_id = #{tenantId}")List<User> selectListByTenantId(@Param("tenantId") Integer tenantId);
}

@TableName注解指定了对应的表名,@TableField注解指定了租户ID字段名。在查询时,我们需要根据租户ID过滤掉不属于当前租户的数据。

☕在进行数据操作时,我们需要设置当前线程的租户ID,例如:

我们通过设置租户ID来选择对应的数据源,然后调用用户Mapper的查询函数来获取对应租户ID的用户列表。

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public List<User> getUserListByTenantId(Integer tenantId) {TenantContextHolder.setTenantId(tenantId.toString()); // 动态设置租户IDtry {return userMapper.selectListByTenantId(tenantId);} finally {TenantContextHolder.clear(); // 清空租户ID}}
}

☕我们需要实现租户ID的动态传递,例如在Web请求中,从请求头或者参数中获取租户ID,然后将租户ID保存到当前线程中,例如:

public class TenantInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String tenantId = request.getHeader("tenantId");if (StringUtils.isNotBlank(tenantId)) {TenantContextHolder.setTenantId(tenantId);}return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {TenantContextHolder.clear();}
}

 

 

☕上面的代码实现了Mybatis-Plus的多租户数据隔离功能,其实现逻辑如下:

  • 配置Mybatis-Plus的多租户支持,通过设置logic-not-delete-value、tenant-column和tenant-handler等参数来启用多租户功能。

  • 自定义租户处理器,实现TenantHandler接口中的getTenantId、getTenantIdColumn和doTableFilter等方法,其中getTenantId方法返回当前线程中保存的租户ID,getTenantIdColumn返回租户ID字段名,doTableFilter方法可以过滤掉一些不需要进行租户隔离的表。

  • 定义数据源,在动态数据源中添加多个数据源,根据租户ID选择对应的数据源。

  • 使用Mybatis-Plus进行数据操作,在Mapper接口中添加@TableName注解和@TableField注解,其中@TableName注解指定了对应的表名,@TableField注解指定了租户ID字段名。在查询时,需要根据租户ID过滤掉不属于当前租户的数据。

  • 实现租户ID的动态设置,通过设置当前线程的租户ID,选择对应的数据源。

  • 实现租户ID的动态传递,在Web请求中,从请求头或者参数中获取租户ID,然后将租户ID保存到当前线程中。使用完后,需要清空当前线程中的租户ID。

☕小伙伴肯定很疑问如何实现的租户隔离,具体在那一步?

在上面的代码中,租户隔离是在使用Mybatis-Plus进行数据操作时实现的。具体来说,是在UserService中的list方法中实现的。

在list方法中,我们通过TenantContextHolder获取当前租户的标识,并构造了一个QueryWrapper对象,加入了租户ID条件。然后调用baseMapper的selectList方法进行查询操作,Mybatis-Plus会自动解析SQL语句中的租户信息,并进行数据隔离。

具体来说,Mybatis-Plus会在SQL语句中加入租户ID条件,以实现数据隔离。例如,如果我们查询用户列表的SQL语句为:

SELECT * FROM user

则在使用Mybatis-Plus进行查询时,会自动加入租户ID条件,变为:

SELECT * FROM user WHERE tenant_id = 'xxx'

其中,xxx为当前租户的标识。这样就可以实现数据隔离,确保不同租户之间的数据不会相互干扰。

综上所述,上面的代码中租户隔离是在使用Mybatis-Plus进行数据操作时实现的,通过在SQL语句中加入租户ID条件,以实现数据隔离。

☕Mybatis-Plus部分函数示例:

☕Mybatis-Plus提供了分页函数,可以方便地进行分页查询,例如:

Page<User> page = new Page<>(1, 10);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("status", 1);
IPage<User> userPage = userMapper.selectPage(page, wrapper);
List<User> userList = userPage.getRecords(); //查询结果
int totalPages = userPage.getPages(); //总页数

☕Mybatis-Plus提供了条件构造器,可以方便地构造查询条件,例如:

QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("status", 1).like("username", "test").between("age", 20, 30).orderByAsc("age");
List<User> userList = userMapper.selectList(wrapper);

☕Mybatis-Plus提供了使用Lambda表达式进行查询的方式,可以更加方便地构造查询条件,例如:

LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
wrapper.eq(User::getStatus, 1).like(User::getUsername, "test").between(User::getAge, 20, 30).orderByAsc(User::getAge);
List<User> userList = userMapper.selectList(wrapper);

 

☕数据库主键生成策略:支持多数据方式以 native 总结组合生成、可单类里直接 使用 @Options 注解,借助增品并发的生成编号办法很舒服, 

public interface UserMapper extends BaseMapper<User> {@Insert("insert into user(name) values(#{name})")@Options(useGeneratedKeys = true, keyProperty = "id")int insertUser(User user);
}


http://www.ppmy.cn/news/648625.html

相关文章

王者荣耀服务器维修多久,王者荣耀今天维护到几点 维护时间详解

王者荣耀新赛季开启之后&#xff0c;维护时间有些长久&#xff01;那么王者荣耀今天维护到几点呢&#xff1f;小伙伴们你们是不是也很想要知道呢&#xff1f;那么现在小编要给你们推荐带来的就是维护时间的详细介绍&#xff0c;小伙伴们来看吧&#xff01; 如果更新不了&#x…

王者荣耀显示聊天服务器异常,王者荣耀功能存在异常暂时关闭怎么回事 解决办法...

王者荣耀功能存在异常暂时关闭怎么回事&#xff0c;功能存在异常暂时关闭解决办法。不知道大家今天在玩游戏的时候&#xff0c;有没有被王者荣耀的系统提醒功能存在异常暂时关闭&#xff0c;很多小伙伴对此一头雾水&#xff0c;不知道怎么解决&#xff0c;小编今天就要为大家解…

Yolov8优化: 多分支卷积模块RFB,扩大感受野提升小目标检测精度

1.RFB-Net介绍 论文&#xff1a;https://arxiv.org/pdf/1711.07767.pdf 代码&#xff1a;GitHub - GOATmessi7/RFBNet: Receptive Field Block Net for Accurate and Fast Object Detection, ECCV 2018 受启发于人类视觉的Receptive Fields结构&#xff0c;本文提出RFB&#xf…

王者荣耀android换ios,王者荣耀安卓转ios教程攻略

在近日&#xff0c;王者荣耀将开放该功能的限量测试&#xff0c;召唤师在这个阶段可以限量申请。当所有功能在限量阶段测试顺利并稳定后&#xff0c;将正式开放该服务。ios转安卓&#xff0c;安卓转ios&#xff0c;终于不是梦了!!! 1.开放时间数量尚未公布 角色转移服务即将开放…

仿写王者荣耀主页代码HTML CSS,CSS3实现王者荣耀匹配人员加载页面的方法

玩过王者的应该都熟悉,这个页面的效果。为什么要实现这个效果了? CSS3实现王者荣耀匹配人员加载页面的方法-1.png (110.5 KB, 下载次数: 0) 2020-7-22 15:21 上传 第一:王者这么火,电竞这么火。 第二:主要还是来学习 CSS3 的线性、径向渐变、旋转、缩放以及动画。 图形解析…

王者荣耀 服务器显示不出100区,王者荣耀为什么荣耀战区显示不出来

因为所在的服务器还没有到开放名单里面&#xff0c;系统会优先开放安卓手Q1-20区、安卓微信1-10区、IOS手Q1-10区、IOS微信1-10区这些&#xff0c;如果玩家服务器不在里面&#xff0c;就没办法开启了。 只有等后续服务器都开放了战区才可以开启&#xff0c;荣耀战区还在测试&am…

鸿蒙os版王者荣耀,王者荣耀鸿蒙版

王者荣耀鸿蒙版下载&#xff0c;快猴网为大家带来的王者荣耀鸿蒙版是为了适配华为的鸿蒙系统而特别设立的版本&#xff0c;玩家可以体验远超一般系统的流畅度&#xff0c;让你的手速能轻松跟上你的意识&#xff0c;享受成为王者荣耀鸿蒙版最强王者的快乐吧! 王者荣耀鸿蒙版游戏…

无线网460王者荣耀服务器,王者荣耀总是460怎么办?

王者荣耀玩家经常在游戏中遇到一个巨抓狂的情形就是关键时刻460&#xff0c;杀敌结果被反杀&#xff0c;砸手机的心都有了啊。明明网络下载的时候很快&#xff0c;玩王者荣耀关键时刻460是怎么回事?王者荣耀网络延迟怎么解决? 在排除路由老化、网络带宽过低以及带宽被多人占用…

用html制作王者荣耀的界面,《王者荣耀》操作设置详解

《王者荣耀》操作设置详解 近期手游平台有一款名为《王者荣耀》&#xff0c;其优质的MOBA体验让无数玩家爱不释手&#xff0c;而其中的操作模式更值得玩家去深究。究竟这款游戏的操作设置有何作用&#xff0c;玩家又该如何利用好操作模式&#xff0c;就一起来看看吧。 操作模式…

小米路由器的未授权访问

01 漏洞产生 小米路由器由于Nginx的配置文件&#xff0c;没有配置好&#xff0c;存在一个目录穿越的漏洞。 在Nginx当中存在该漏洞&#xff0c;配置如下&#xff1a; location /xxx {alias /abc/; }02 fofa搜索 app“小米路由器” 03 漏洞利用 如此配置&#xff0c;攻击者…

解决win10开机卡顿、配置很高但是玩游戏卡顿掉帧等问题

解决win10开机卡顿、配置很高但是玩游戏卡顿掉帧等问题 最近组装了一台高配置的新电脑&#xff0c;装好了各种驱动、软件等。发现系统开机后卡顿一分钟左右&#xff08;加载应用配置等&#xff09;&#xff0c;但是我的系统启动项明明就没多少&#xff0c;不应该是这样的情况&…

python实现基于SVD矩阵分解的电影推荐系统设计

大家好&#xff0c;我是带我去滑雪&#xff01; SVD 是一种矩阵分解技术&#xff0c;通过将空间维数从 N 维降到 K 维&#xff08;其中K<N&#xff09;来减少数据集的特征数。在推荐系统中&#xff0c;SVD 被用作一种协同过滤技术。使用矩阵结构&#xff0c;其中行表示用户&…

树莓派使用Nginx 搭建轻量级网站远程访问

文章目录 1. Nginx安装2. 安装cpolar3.配置域名访问Nginx4. 固定域名访问5. 配置静态站点 转载自cpolar极点云文章&#xff1a;树莓派使用Nginx 搭建轻量级网站远程访问 安装 Nginx&#xff08;发音为“engine-x”&#xff09;可以将您的树莓派变成一个强大的 Web 服务器&#…

crashrpt

今天原本打算在谷歌上搜索处理SEH的文章&#xff0c;以使我不需要在每一个线程中使用__try{}__except()代码块包裹代码的情况下&#xff0c;就能在任意线程抛出SEH时生成MiniDump文件。不过最后的结果是处理SEH的文章没有搜索出几篇&#xff0c;却幸运的搜索出了满足我需要的工…

Windows平台崩溃转储系统crashrpt的使用

概述 CrashRpt 是一个免费的、轻量级的开源错误报告库开源库&#xff0c;旨在拦截C程序中的异常&#xff0c;收集有关崩溃的技术信息并通过互联网向软件供应商发送错误报告&#xff0c;用于在 Microsoft Visual Studio IDE 中创建并在 Windows 中运行C应用程序。&#xff08;不…

【Crash】C++程序崩溃排查方法

windows下C++程序release版本崩溃错误排查方法。 一个你精心设计的24小时不间断运行,多线程的程序,突然运行了几个月后崩了,此问题是非常难以排查的,也是很头疼的问题。 现利用Google开源工具crashrpt与Microsoft windbg工具,解决这个问题,并分享给大家。 使用工具Crashrp…

CrashRpt开源代码使用详解(一):CrashRpt介绍及简单应用

CrashRpt开源代码使用详解&#xff08;一&#xff09;&#xff1a;CrashRpt介绍及简单应用 1、简介 CrashRpt是一个开源的第三方包&#xff0c;在程序出现未处理异常时&#xff0c;能够收集错误信息&#xff0c;并生成程序错误报告。CrashRpt可以将报告按照指定的方式(例如HTT…

Unity UGUI Canvas Overlay模式获取屏幕坐标

UGUI Canvas Overlay模式获取屏幕坐标 &#x1f354;效果&#x1f371;获取 &#x1f354;效果 &#x1f371;获取 ui的position就是屏幕坐标(●’◡’●) var screenPos new Vector2(transform.position.x, transform.position.y);

win10游戏不能窗口化怎么办?全屏游戏切换窗口的方法

通常&#xff0c;win10电脑玩游戏时都会把全屏游戏切换窗口&#xff0c;以方便我们进行其它操作&#xff0c;可以实现边玩边聊天的状态。但是有个用户反馈说win10游戏不能窗口化&#xff0c;也不知道是怎么回事&#xff1f;多次尝试还是一样的结果&#xff0c;如何解决呢&#…

windows10 强制关闭全屏游戏卡死的方法

玩游戏时&#xff0c;游戏卡死后会依旧强行占据在窗口的&#xff0c;按ALTF4也没反应&#xff0c;就算ctrlaltdel调出任务管理器也会被挡住&#xff0c;真的烦死了&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff…