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

ruoyi-cloud 集成 mybatis-plus 多租户插件:从配置到实战避坑指南

1. 为什么需要多租户插件?

在开发SaaS应用时,数据隔离是最基础也是最重要的需求之一。想象一下,你开发了一个CRM系统,A公司和B公司同时使用你的系统,但A公司绝对不能看到B公司的客户数据,这就是典型的多租户场景。

我去年接手过一个项目,客户要求两周内实现多租户隔离。当时尝试了几种方案:独立数据库(成本太高)、共享数据库独立Schema(维护麻烦),最终选择了共享数据库共享表的方案,这就是Mybatis-Plus多租户插件的用武之地。

Mybatis-Plus的多租户插件通过在SQL中自动注入租户条件(比如WHERE tenant_id = 'A')来实现数据隔离。这种方式既保证了数据安全,又不会增加太多开发负担。在RuoYi-Cloud这种成熟的微服务框架中集成该插件,能快速为你的SaaS应用加上"数据隔离盾"。

2. 环境准备与依赖管理

2.1 版本兼容性:躲开第一个大坑

我最开始集成时被版本冲突折腾得够呛。Mybatis-Plus和PageHelper都依赖jsqlparser,但版本不一致会导致经典的ClassCastException

// 典型报错 net.sf.jsqlparser.statement.select.SetOperationList cannot be cast to net.sf.jsqlparser.statement.select.PlainSelect

经过多次测试,总结出黄金组合:

<properties> <!-- Mybatis-Plus要主导jsqlparser版本 --> <com.baomidou.mybatisplus.version>3.5.2</com.baomidou.mybatisplus.version> <!-- PageHelper版本要低于Mybatis-Plus --> <pagehelper.boot.version>1.4.2</pagehelper.boot.version> </properties>

2.2 依赖配置技巧

dependencyManagement中声明依赖时,记得给PageHelper做排除:

<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <exclusions> <exclusion> <artifactId>mybatis-spring</artifactId> <groupId>org.mybatis</groupId> </exclusion> <exclusion> <artifactId>mybatis</artifactId> <groupId>org.mybatis</groupId> </exclusion> </exclusions> </dependency>

这样能避免Mybatis核心库的版本冲突。建议新建一个mybatis-plus模块专门管理相关依赖,其他模块按需引入。

3. 核心配置实战

3.1 插件配置类详解

配置类的编写有几个关键点需要注意:

@Configuration @EnableConfigurationProperties(MyTenantConfigProperties.class) @AllArgsConstructor // 推荐使用构造器注入 public class MybatisPlusConfig { private final MyTenantConfigProperties tenantConfig; @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 注意插件顺序:多租户插件必须在前! interceptor.addInnerInterceptor(new TenantLineInnerInterceptor( new CustomTenantLineHandler(tenantConfig))); // 分页插件要放在后面 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }

踩坑提醒

  1. 插件顺序影响SQL生成,多租户插件必须最先添加
  2. 避免使用@Autowired字段注入,推荐构造器注入
  3. 分页插件要设置DbType,否则某些复杂SQL会解析错误

3.2 租户处理器编写秘籍

自定义处理器是业务逻辑的核心,这里分享我的实战代码:

@Component @RequiredArgsConstructor public class CustomTenantLineHandler implements TenantLineHandler { private final MyTenantConfigProperties config; private final SecurityUtils securityUtils; // 你的权限工具类 @Override public String getTenantIdColumn() { return config.getColumn(); // 默认tenant_id } @Override public boolean ignoreTable(String tableName) { // 超级管理员跳过过滤 if (securityUtils.isSuperAdmin()) { return true; } // 配置表不参与租户过滤 return config.getTables().contains(tableName); } @Override public Expression getTenantId() { // 从登录信息获取租户ID return new StringValue(securityUtils.getTenantId()); } }

避坑指南

  • 对于sys_config这类系统表,一定要配置忽略
  • 租户ID建议从ThreadLocal获取,避免跨线程问题
  • 日志打印租户ID方便调试,但生产环境记得关闭

4. 动态配置与Nacos集成

4.1 配置热更新方案

通过Nacos实现配置动态刷新:

@Data @RefreshScope @ConfigurationProperties(prefix = "ruoyi.tenant") public class MyTenantConfigProperties { private String column = "tenant_id"; private List<String> tables = new ArrayList<>(); }

对应的Nacos配置:

ruoyi: tenant: column: tenant_code # 支持修改租户字段名 tables: - sys_config - sys_dict_data

实测效果

  • 修改配置后30秒内生效
  • 新增忽略表不需要重启服务
  • 租户字段名可随时变更

4.2 代码生成器改造

RuoYi的代码生成器需要做两处修改:

  1. 实体类自动添加租户字段注解:
@TableField(fill = FieldFill.INSERT) private String tenantId;
  1. Mapper模板中加入租户过滤:
<where> ${ew.sqlSegment} AND tenant_id = #{tenantId} </where>

建议新建一个代码生成模板,避免污染原有模板。我在实际项目中专门创建了tenant-mapper.xml.vm模板文件。

5. 常见问题排查手册

问题1:新增数据时报错tenant_id不能为null

  • 检查实体类是否有@TableField(fill = FieldFill.INSERT)
  • 确认MetaObjectHandler配置了自动填充

问题2:多表联查时租户条件丢失

  • 使用LEFT JOIN时要手动添加ON条件:
LEFT JOIN table_b b ON a.id = b.a_id AND b.tenant_id = 'xxx'

问题3:批量插入性能差

  • 改用Mybatis-Plus的saveBatch方法
  • 开启批处理模式:
mybatis-plus: global-config: db-config: logic-delete-field: delFlag configuration: default-executor-type: batch

问题4:分布式事务问题

  • 建议使用Seata时关闭多租户插件对undo_log表的过滤
  • 在handler中特殊处理undo_log表:
if ("undo_log".equals(tableName)) { return true; }

记得在测试环境充分验证这些场景。我在上线前专门编写了20多个集成测试用例,覆盖了各种边界情况。

http://www.jsqmd.com/news/489971/

相关文章:

  • 开箱即用!Fish-Speech-1.5镜像部署,无需代码基础
  • Flutter Camera插件实战:如何避免全屏预览画面变形(附完整代码)
  • 【H5 前端开发笔记】第 05 期:HTML常用标签 (1) 文档定义标签
  • 个人相册色彩修复:cv_unet_image-colorization 工具实测与使用技巧
  • Qwen-Ranker Pro与Kubernetes集成:云原生部署实践
  • Win10系统下N卡1070显卡深度学习环境配置:CUDA8.0/9.1与cuDNN5.1/7.0共存指南
  • 【ROS进阶】- tf核心函数实战解析:从坐标查询到点云转换
  • 【H5 前端开发笔记】第 06 期:HTML常用标签 (2) 文本标签、图片标签
  • DA14585开发实战:从Keil5编译到SmartSnippets Toolbox烧录全解析
  • Qwen3.5-27B多模态落地:跨境电商商品图→多语言描述→合规性检查
  • Colmap在AutoDL云服务器上的完整安装指南(含常见报错解决方案)
  • 企业级工单管理零成本解决方案:osTicket从部署到精通指南
  • 实战Node.js实时应用,基于快马平台快速构建Socket.io聊天室后端
  • Z-Image-GGUF多场景:海报设计/社交头像/产品展示/教学插图全链路覆盖
  • 逆向工程入门:手把手教你绕过CRC检测(CheatEngine实战)
  • 激光雷达建图避坑指南:二值贝叶斯滤波中的逆测量模型到底怎么用?
  • Swin2SR使用体验:内置防崩溃机制,大图处理也不怕
  • Coze数据库实战:5分钟搭建一个AI客服系统的数据存储方案
  • AI辅助开发实战:CiteSpace关键词聚类自动化处理与优化
  • 小米ReCogDrive实战:如何用扩散模型解决自动驾驶的轨迹规划难题?
  • PowerBI日期表全攻略:从CALENDAR到时间智能函数的完整实践
  • 优优推联系方式查询:探讨数字营销服务使用指南 - 十大品牌推荐
  • 从ElementPlus警告看前端数据清洗:el-pagination的total传值避坑指南
  • 重庆帕金森治疗
  • ROS导航实战:如何用move_base让机器人避开办公室障碍物(附避坑指南)
  • Mirage Flow辅助LaTeX学术论文写作:从数据到出版级排版
  • 我曾被当作抹布,而她,不过是块最虚伪的脏抹布
  • AcousticSense AI真实作品:世界音乐(World)多乐器叠奏频谱的空间分离效果
  • 3大核心功能破解抖音内容采集难题:从技术原理到实战应用的完整指南
  • 用快马AI快速原型一个高转化广告落地页,十分钟搞定演示