
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.version3.5.2/com.baomidou.mybatisplus.version !-- PageHelper版本要低于Mybatis-Plus -- pagehelper.boot.version1.4.2/pagehelper.boot.version /properties2.2 依赖配置技巧在dependencyManagement中声明依赖时记得给PageHelper做排除dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper-spring-boot-starter/artifactId exclusions exclusion artifactIdmybatis-spring/artifactId groupIdorg.mybatis/groupId /exclusion exclusion artifactIdmybatis/artifactId groupIdorg.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; } }踩坑提醒插件顺序影响SQL生成多租户插件必须最先添加避免使用Autowired字段注入推荐构造器注入分页插件要设置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 ListString tables new ArrayList(); }对应的Nacos配置ruoyi: tenant: column: tenant_code # 支持修改租户字段名 tables: - sys_config - sys_dict_data实测效果修改配置后30秒内生效新增忽略表不需要重启服务租户字段名可随时变更4.2 代码生成器改造RuoYi的代码生成器需要做两处修改实体类自动添加租户字段注解TableField(fill FieldFill.INSERT) private String tenantId;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多个集成测试用例覆盖了各种边界情况。