mybatisplus多租户原理略解
概述
当前mybatisPlus版本
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.2</version>
</dependency>
jdk版本:17
springboot版本:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>3.1.2</version>
</dependency>
业务中想按照租户划分权限时,一般简单的就是在表里加个字段,但是这样每个sql语句都要改造,很不方便。
mybatisplus有个现成的租户插件
com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor
在配置租户插件之前,需要在token中先塞入租户id,方便后面在拦截器中获取当前用户的租户.
租户注入的关键代码在
com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor#buildTableExpression

配置
配置的时候租户拦截器需要放在第一个,即`index=0
package org.qps.common.tenant.handle;import cn.hutool.core.collection.ListUtil;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import org.qps.common.core.utils.StringUtils;
import org.qps.common.satoken.utils.LoginHelper;
import org.qps.common.tenant.helper.TenantHelper;
import org.qps.common.tenant.properties.TenantProperties;
import lombok.AllArgsConstructor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.StringValue;import java.util.List;/*** 自定义租户处理器**/
@AllArgsConstructor
public class PlusTenantLineHandler implements TenantLineHandler {private final TenantProperties tenantProperties;@Overridepublic Expression getTenantId() {String tenantId = LoginHelper.getTenantId();if (StringUtils.isBlank(tenantId)) {return new NullValue();}String dynamicTenantId = TenantHelper.getDynamic();if (StringUtils.isNotBlank(dynamicTenantId)) {// 返回动态租户return new StringValue(dynamicTenantId);}// 返回固定租户return new StringValue(tenantId);}@Overridepublic boolean ignoreTable(String tableName) {String tenantId = LoginHelper.getTenantId();// 判断是否有租户if (StringUtils.isNotBlank(tenantId)) {// 不需要过滤租户的表List<String> excludes = tenantProperties.getExcludes();// 非业务表List<String> tables = ListUtil.toList("gen_table","gen_table_column");tables.addAll(excludes);return tables.contains(tableName);}return true;}}package org.qps.common.tenant.config;import cn.dev33.satoken.dao.SaTokenDao;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.qps.common.core.utils.reflect.ReflectUtils;
import org.qps.common.mybatis.config.MybatisPlusConfig;
import org.qps.common.redis.config.RedisConfig;
import org.qps.common.redis.config.properties.RedissonProperties;
import org.qps.common.tenant.core.TenantSaTokenDao;
import org.qps.common.tenant.handle.PlusTenantLineHandler;
import org.qps.common.tenant.handle.TenantKeyPrefixHandler;
import org.qps.common.tenant.manager.TenantSpringCacheManager;
import org.qps.common.tenant.properties.TenantProperties;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.SingleServerConfig;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;import java.util.ArrayList;
import java.util.List;/*** 租户配置类** TenantProperties 自定义租户配置对象* MybatisPlusConfig 自定义mybatisPlus配置,维护了分页拦截器等基础拦截器*/
@EnableConfigurationProperties(TenantProperties.class)
@AutoConfiguration(after = {MybatisPlusConfig.class})
@ConditionalOnProperty(value = "tenant.enable", havingValue = "true")
public class TenantConfig {/*** 初始化租户配置*/@Beanpublic boolean tenantInit(MybatisPlusInterceptor mybatisPlusInterceptor,TenantProperties tenantProperties) {List<InnerInterceptor> interceptors = new ArrayList<>();// 多租户插件 必须放到第一位interceptors.add(tenantLineInnerInterceptor(tenantProperties));interceptors.addAll(mybatisPlusInterceptor.getInterceptors());mybatisPlusInterceptor.setInterceptors(interceptors);return true;}/*** 多租户插件*/public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) {return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties));}}
多租户拦截忽略
启用多租户之后,有些业务sql想查询多个租户的数据或者不想被注入租户id。
那么可以加上com.baomidou.mybatisplus.annotation.InterceptorIgnore
@InterceptorIgnore(tenantLine = "true")
即可忽略
原理可以参考源码
com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor#beforeQuery
com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper#willIgnoreTenantLine
com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper#willIgnore
将注解数据初始化到内存中
com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper#initSqlParserInfoCache(java.lang.Class<?>)
相关文章:
mybatisplus多租户原理略解
概述 当前mybatisPlus版本 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.2</version> </dependency>jdk版本:17 springboot版本:…...
Spring整合RabbitMQ-配制文件方式-1-消息生产者
Spring-amqp是对AMQP的一些概念的一些抽象,Spring-rabbit是对RabbitMQ操作的封装实现。 主要有几个核心类RabbitAdmin、RabbitTemplate、SimpleMessageListenerContainer等 RabbitAdmin类完成对Exchange、Queue、Binding的操作,在容器中管理 了RabbitA…...
Python Opencv实践 - 凸包检测(ConvexHull)
import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/stars.png") plt.imshow(img[:,:,::-1])img_contour img.copy() #得到灰度图做Canny边缘检测 img_gray cv.cvtColor(img_contour, cv.COLOR_BGR2GRAY) edges…...
IP网络广播系统有哪些优点
IP网络广播系统有哪些优点 IP网络广播系统有哪些优点? IP网络广播系统是基于 TCP/IP 协议的公共广播系统,采用 IP 局域网或 广域网作为数据传输平台,扩展了公共广播系统的应用范围。随着局域网络和 网络的发展 , 使网络广播的普及变为可能 …...
【LeetCode】83. 删除排序链表中的重复元素
83. 删除排序链表中的重复元素(简单) 方法:一次遍历 思路 由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。 从指针 cur 指…...
【大数据】Flink 详解(七):源码篇 Ⅱ
本系列包含: 【大数据】Flink 详解(一):基础篇【大数据】Flink 详解(二):核心篇 Ⅰ【大数据】Flink 详解(三):核心篇 Ⅱ【大数据】Flink 详解(四…...
stable diffusion实践操作-SD原理
系列文章目录 本文专门开一节写SD原理相关的内容,在看之前,可以同步关注: stable diffusion实践操作 文章目录 系列文章目录前言一、原理说明1.1、出图原理1.1.1 AI画画不是和人一样,从0开始,而是一个去噪点的过程&am…...
C++ Primer Plus第十三章编程练习答案
1,以下面的类声明为基础: // base class class Cd{ // represents a CD disk private: char performers[50] ; char label[20]; int selections;// number of selections double playtime; // playing time in minutes public: Cd(char * sl,char * s2,int n,double…...
Elasticsearch:wildcard - 通配符搜索
Elasticsearch 是一个分布式、免费和开放的搜索和分析引擎,适用于所有类型的数据,例如文本、数字、地理空间、结构化和非结构化数据。 它基于 Apache Lucene 构建,Apache Lucene 是一个全文搜索引擎,可用于各种编程语言。 由于其速…...
配置类安全问题学习小结
目录 一、前言 二、漏洞类型 目录 一、前言 二、漏洞类型 2.1 Strict Transport Security Not Enforced 2.2 SSL Certificate Cannot Be Trusted 2.3 SSL Anonymous Cipher Suites Supported 2.4 "Referrer Policy”Security 头值不安全 2.5 “Content-Security-…...
IMX6ULL移植篇-uboot源码目录
一. uboot 源码分析前提 由于 uboot 会使用到一些经过编译才会生成的文件,因此,我们在分析 uboot的时候,需要先编译一下 uboot 源码工程。 这里所用的开发板是 nand-flash版本。 二. uboot 源码目录及编译 1. uboot 源码目录 uboot源码目…...
SAP MM学习笔记27- 购买依赖(采购申请)
前面已经努力的学习了 购买发注,入库,请求书照合 等功能,还是蛮多内容的哈。 剩下的功能,比如 右侧的 所要量决定,供给元决定,仕入先选择 还没学。 从这章开始,要开始学习它们了。 这一章先来…...
C++零碎记录(八)
14. 运算符重载简介 14.1 运算符重载简介 ① 运算符重载:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。 ② 对于内置的数据类型的表达式的运算符是不可能改变的。 14.2 加号运算符重载 ① 加号运算符作用&#x…...
基于matlab的扩频解扩误码率完整程序分享
clc; clear; close all; warning off; addpath(genpath(pwd)); r5; N2^r-1;%周期31 aones(1,r); mzeros(1,N); for i1:(2^r-1) temp mod((a(5)a(2)),2); for jr:-1:2 a(j)a(j-1); end a(1)temp; m(i)a(r); end mm*2-1;%双极性码 %产生随…...
算法:轮转数组---循环取模运算
1、题目: 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 2、分析特点: 轮转 > 取模运算 我们可以使用额外的数组来将每个元素放至正确的位置。用 n 表示数组的长度,我们遍历原数组&a…...
Vue教程
官网vue快速上手 vue示例图 请点击下面工程名称,跳转到代码的仓库页面,将工程 下载下来 Demo Code 里有详细的注释 代码:LearnVue...
算法之双指针题型:
双指针例题小总结: 力扣27: 移除元素 力扣题目链接 双指针分为: 快慢双指针:同一个起点,同向出发 相向双指针:从两端出发,方向相反,终会相遇 经典的双指针(快慢双指…...
vue传递给后端时间格式问题
前端处理 首先前端使用moment.js进行处理 data.userEnrolDate moment(data.userEnrolDate).format(YYYY-MM-DD HH:mm:ss);后端处理 JsonFormat(timezone "GMT8", pattern "yyyy-MM-dd HH:mm:ss") DateTimeFormat(pattern "yyyy-MM-dd HH:mm:ss…...
php使用jwt作登录验证
1 在项目根目录下,安装jwt composer require firebase/php-jwt 2 在登录控制器中加入生成token的代码 use Firebase\JWT\JWT; use Firebase\JWT\Key; class Login extends Cross {/*** 显示资源列表** return \think\Response*/public function index(Request $r…...
【zlm】 PTS DTS
在音视频编码和传输中,PTS(Presentation Time Stamp)和DTS(Decoding Time Stamp)是两个关键的时间戳,用于确保音视频帧的顺序和同步。它们在多媒体处理中扮演重要的角色: PTS(Presen…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined.
这个警告表明您在使用Vue的esm-bundler构建版本时,未明确定义编译时特性标志。以下是详细解释和解决方案: 问题原因: 该标志是Vue 3.4引入的编译时特性标志,用于控制生产环境下SSR水合不匹配错误的详细报告1使用esm-bundler…...
