当前位置: 首页 > news >正文

mybatis-plus 多租户方案1使用和坑注意事项,方案是需要实现租户功能的表都增加租户id字段

1 前提springboot整合好 mybatis-plus (版本3.5.4)
   需要实现多租户的表,添加修改对应字段和 pojo类 (表添加tenant_id字段,pojo添加tenantId属性)

配置文件更改,方便扩展

#多租户配置
tenant:enable: truecolumn: tenant_idignoreTables:- tb_order- users

属性文件配置类

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;/*** 多租户配置属性类*/
@Component
@Data
@ConfigurationProperties(prefix = "tenant")
public class TenantProperties {/*** 是否开启多租户*/private Boolean enable;/*** 租户id字段名*/private String column;/*** 需要忽略的多租户的表,此配置优先filterTables,若此配置为空则启用filterTables*/private List<String> ignoreTables;}

4 租户id管理类,从threadlocal 中取,这个根据业务情况来

/*** 租户上下文持有者,用于管理当前线程的租户ID。* 提供了获取、设置和清除当前线程租户ID的方法。*/
public class TenantContextHolder {// 静态ThreadLocal变量,用于存储当前线程的租户IDstatic ThreadLocal<Long> tenantThreadLocal = new ThreadLocal<>();/*** 获取当前线程的租户ID。** @return 当前线程的租户ID*/public static Long getCurrentTenantId() {return tenantThreadLocal.get();}/*** 设置当前线程的租户ID。** @param tenantId 要设置的租户ID*/public static void setCurrentTenantId(Long tenantId) {tenantThreadLocal.set(tenantId);}/*** 清除当前线程的租户ID,使得线程不再关联任何租户。*/public static void clear() {tenantThreadLocal.remove();}
}

多租户处理器实现,核心类

import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.List;/*** 多租户处理器实现,核心类*/
@Component
public class MultiTenantHandler implements TenantLineHandler {@Autowiredprivate TenantProperties tenantProperties;/*** 根据表名判断是否忽略拼接多租户条件* 默认都要进行解析并拼接多租户条件** @param tableName 表名* @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件*/@Overridepublic boolean ignoreTable(String tableName) {//忽略指定表对租户数据的过滤List<String> ignoreTables = tenantProperties.getIgnoreTables();if (null != ignoreTables && ignoreTables.contains(tableName)) {return true;}return false;}/*** 获取租户ID值表达式,只支持单个ID值 (实际应该从用户信息中获取)* @return 租户ID值表达式*/@Overridepublic Expression getTenantId() {if(TenantContextHolder.getCurrentTenantId()!=null){Long tenantId = TenantContextHolder.getCurrentTenantId();if(tenantId!=null){return new LongValue(tenantId);}}//设默认的值 或 异常,灵活处理return new LongValue(0);}/*** 获取租户字段名,默认字段名叫: tenant_id* @return 租户字段名*/@Overridepublic String getTenantIdColumn() {return tenantProperties.getColumn();}}

mybatis 拦截器配置,核心类

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MybatisPlusConfig {@Autowiredprivate MultiTenantHandler tenantHandler;@Autowiredprivate TenantProperties tenantProperties;@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();if (tenantProperties.getEnable()) {// 添加多租户插件interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(tenantHandler));}//分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//防止全表更新与删除插件: BlockAttackInnerInterceptorBlockAttackInnerInterceptor blockAttackInnerInterceptor = new BlockAttackInnerInterceptor();interceptor.addInnerInterceptor(blockAttackInnerInterceptor);return interceptor;}
}

不需要租户的方法 (此注解只能放在mapper层的接口方法中),可以添加 如下配置,如,生成的sql就不会拼接租户的字段

@InterceptorIgnore(tenantLine = "true")
List<Book> searchAllBook_diySql();

写个springboot 拦截器或用其它方案,如拦截器里,将header中的租户id,设置到TenantContextHolder.setCurrentTenantId里面去

开始测试,需要使用租户功能的,在header中添加 租户id
在增删改查操作时,sql都会拼接上 租户id的条件



坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑


1 自己写的sql ,不要加租户id字段,不要加租户id字段,不要加租户id字段,系统会自己拼接sql

这个示例,租户id都是统一添加在header中,拦截器再从header中取租户id,并设置到TenantContextHolder.setCurrentTenantId里面去,某些方法又在参数里如 requestParam或RequestBody中设置了租户id,会引发数据错乱,这边是使用了一个aop拦截器,将参数中传递的租户id,统一设为null,如下

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;@Aspect
@Component
public class TenantIdAspect {@Before("execution(* cn.baidu.sharding_jdbc_test.control..*.*(..)) && args(*)")public void cleanTenantId(JoinPoint joinPoint) {Object[] args = joinPoint.getArgs();for (Object arg : args) {// 使用反射来设置租户ID为nullif (arg != null) {try {String fieldname = "tenantId"; // 假设所有实体类中租户ID的字段名为tenantIdField field = arg.getClass().getDeclaredField(fieldname);field.setAccessible(true);field.set(arg, null);} catch (NoSuchFieldException | IllegalAccessException e) {// 处理异常,例如记录日志e.printStackTrace();}}}}
}

相关文章:

  • 【Linux多线程】线程的终止、等待和分离
  • Bond 网卡绑定技术学习
  • k8s-CCE创建工作负载变量引用
  • jquery.datetimepicker无法添加清除按钮的问题
  • eNSP学习——RIP的路由引入
  • 记录一下npm安装时的错误排查过程
  • 2024-06-06 问AI: 在深度学习中,什么是欧几里德长度?
  • QT串口调试助手V2.0(源码全开源)--上位机+多通道波形显示+数据保存(优化波形显示控件)
  • 【全开源】云调查考试问卷系统(FastAdmin+ThinkPHP+Uniapp)
  • 【C语言从入门到入土】第八章 结构体
  • 【全开源】多功能投票小程序系统源码(ThinkPHP+FastAdmin+Uniapp)
  • 软件安全技术【太原理工大学】
  • Java面向对象-方法的重写、super
  • 【C++】<知识点> C++11新特性
  • 视觉大模型(VLLM)学习笔记
  • 4. 路由到控制器 - Laravel从零开始教程
  • android百种动画侧滑库、步骤视图、TextView效果、社交、搜房、K线图等源码
  • Java 最常见的 200+ 面试题:面试必备
  • Java小白进阶笔记(3)-初级面向对象
  • js作用域和this的理解
  • KMP算法及优化
  • node和express搭建代理服务器(源码)
  • Puppeteer:浏览器控制器
  • React Native移动开发实战-3-实现页面间的数据传递
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 京东美团研发面经
  • 面试遇到的一些题
  • 设计模式走一遍---观察者模式
  • 一、python与pycharm的安装
  • nb
  • 【运维趟坑回忆录】vpc迁移 - 吃螃蟹之路
  • # Apache SeaTunnel 究竟是什么?
  • (1) caustics\
  • (2)MFC+openGL单文档框架glFrame
  • (LeetCode C++)盛最多水的容器
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • *** 2003
  • .Net FrameWork总结
  • .sh 的运行
  • ??javascript里的变量问题
  • @DependsOn:解析 Spring 中的依赖关系之艺术
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka
  • [20150321]索引空块的问题.txt
  • [AIGC] Redis基础命令集详细介绍
  • [C++] 统计程序耗时
  • [C语言][PTA基础C基础题目集] strtok 函数的理解与应用
  • [Excel] vlookup函数
  • [HDU 3555] Bomb [数位DP]
  • [hive小技巧]同一份数据多种处理
  • [iOS]-UIKit
  • [javascript]Tab menu实现
  • [LVGL]:MACOS下使用LVGL模拟器