Java进阶指南
表达式解析
UEL 统一表达式语言
Ognl 对象图导航语言
Spel Spring表达式语言
Java 进阶
SPI的高级用法
SLF4J的绑定原理
H2 JDBC驱动类注册与数据库引擎初始化原理
Java SPI与Dubbo SPI区别
Java 秒懂对象 PO、VO、BO、DTO、POJO!
Java POJO/DO/DTO/BO/VO概念及应用案例分析
一个线程oom,进程里其他线程还能运行吗?
jps命令详解
Java的BigDecimal也会存在丢失精度的问题
java中的枚举类和常量类区别在哪儿?
Java 打包 FatJar 方法小结
"too many open files"的原理和解决方案
GraalVM 专栏
GraalVM入门以及环境搭建
Maven 专栏
maven 跳过单元测试-maven.test.skip和skipTests的区别
maven 配置代码检查插件,生成检查报告
Maven 执行生命周期
maven 删除本地仓库当前项目的依赖包
Gradle 专栏
自己动手应用Groovy实现Gradle的DSL—Task定义
看懂Gradle脚本(1)- Groovy语言的Map语法糖
看懂Gradle脚本(2)- Groovy语言的闭包语法
看懂Gradle脚本(3)- Groovy AST转换
看懂Gradle脚本(4)- Groovy语法之运算符重载
看懂Gradle脚本(5)- 跟Gradle学领域驱动设计
看懂Gradle脚本(6)- Hello Groovy, Goodbye Getters&Setters
看懂Gradle脚本(7)- ext {}函数是如何实现的
Gradle 常见问题集锦
Spring 专栏
Spring AOP 使用介绍,从前世到今生
Spring IOC 容器源码分析
Spring AOP 源码解析
Spring @PropertySource 注解实现读取 yml 文件
Spring 好用的工具类
Spring @Async失效情况
Spring I/O 2023 干货视频精选!
Spring 动态刷新bean
Spring Cache缓存技术
Spring @Transactional注解失效情况
Spring Event 事件订阅踩坑
循环依赖
Spring 解析@Async引起的循环依赖
Spring 中的循环依赖
从源码层面深度剖析 Spring 循环依赖 | 京东云技术团队
Spring 不同平台构建出现循环依赖错误问题原因分析
SpringBoot 专栏
SpringBoot 构建FarJAR Maven配置
SpringBoot 项目启动慢原因分析
SpringBoot 资源文件问题总结(Spring Boot的静态资源访问,配置文件外置)
SpringBoot 读取Jar包中静态资源原理
SpringBoot 配置Undertow处理高并发
SpringBoot Maven Profile配合Spring Profile进行多环境配置和打包
SpringBoot 使用profile结合maven实现多环境配置
SpringBoot @ComponentScan注解过滤排除不加载某个类的3种方法
Mybatis 专栏
Mybatis 一级、二级缓存机制
Mybatis 关闭一级、二级缓存机制
MybatisPlus
MybatisPlus LambdaQueryWrapper类的实现原理
MybatisPlus 在不修改全局策略和字段注解的情况下将字段更新为null
并发与多线程
Java 从单核到多核的多线程并发
并发和并行的区别
Redisson 专栏
一次生产redisson 延时队列不消费问题排查
redisson 阻塞队列不消费问题排查
Spring Batch 专栏
批处理框架spring batch基础知识介绍
Shiro 专栏
一篇适合小白的Shiro教程
SpringMVC 专栏
SpringMVC 后端处理多文件上传如何保持最大的灵活性
@RequestParam的加与不加的作用
SpringCloud 专栏
Gateway 一文彻底解决跨域问题
ruoyi-vue-pro 开发指南
萌新必读
简介
交流群
视频教程
功能列表
快速启动(后端项目)
快速启动(前端项目)
接口文档
技术选型
项目结构
代码热加载
一键改包
删除功能
内网穿透
达梦数据库专属
后端手册
新建模块
代码生成【单表】(新增功能)
代码生成【主子表】
代码生成【树表】
功能权限
数据权限
用户体系
三方登录
OAuth 2.0(SSO 单点登录)
SaaS多租户【字段隔离】
SaaS 多租户【数据库隔离】
WebSocket 实时通讯
异常处理(错误码)
参数校验、时间传参
分页实现
VO 对象转换、数据翻译
文件存储(上传下载)
Excel 导入导出
操作日志、访问日志、异常日志
MyBatis 数据库
MyBatis 联表&分页查询
多数据源(读写分离)、事务
Redis 缓存
本地缓存
异步任务
分布式锁
幂等性(防重复提交)
请求限流(RateLimiter)
单元测试
验证码
工具类
配置管理
数据库文档
中间件手册
定时任务
消息队列(内存)
消息队列(Redis)
消息队列(RocketMQ)
消息队列(RabbitMQ)
消息队列(Kafka)
限流熔断
工作流手册
工作流演示
功能开启
工作流(达梦适配)
审批接入(流程表单)
审批接入(业务表单)
流程设计器(BPMN)
流程设计器(钉钉、飞书)
选择审批人、发起人自选
会签、或签、依次审批
流程发起、取消、重新发起
审批通过、不通过、驳回
审批加签、减签
审批转办、委派、抄送
执行监听器、任务监听器
流程表达式
流程审批通知
大屏手册
报表设计器
大屏设计器
支付手册
功能开启
支付宝支付接入
微信公众号支付接入
微信小程序支付接入
支付宝、微信退款接入
会员手册
功能开启
微信公众号登录
微信小程序登录
会员用户、标签、分组
会员等级、积分、签到
商城手册
商城演示
功能开启
商城装修
【商品】商品分类
【商品】商品属性
【商品】商品 SPU 与 SKU
【商品】商品评价
【交易】购物车
【交易】交易订单
【交易】售后退款
【交易】快递发货
【交易】门店自提
【交易】分销返佣
【营销】优惠劵
【营销】拼团活动
【营销】秒杀活动
【营销】砍价活动
【营销】满减送
【营销】限时折扣
【营销】内容管理
【统计】会员、商品、交易统计
ERP手册
ERP 演示
功能开启
【产品】产品信息、分类、单位
【库存】产品库存、库存明细
【库存】其它入库、其它出库
【库存】库存调拨、库存盘点
【采购】采购订单、入库、退货
【销售】销售订单、出库、退货
【财务】采购付款、销售收款
CRM 手册
CRM 演示
功能开启
【线索】线索管理
【客户】客户管理、公海客户
【商机】商机管理、商机状态
【合同】合同管理、合同提醒
【回款】回款管理、回款计划
【产品】产品管理、产品分类
【通用】数据权限
【通用】跟进记录、待办事项
公众号手册
功能开启
公众号接入
公众号粉丝
公众号标签
公众号消息
自动回复
公众号菜单
公众号素材
公众号图文
公众号统计
系统手册
短信配置
邮件配置
站内信配置
数据脱敏
敏感词
地区 & IP 库
运维手册
开发环境
Linux 部署
Docker 部署
Jenkins 部署
HTTPS 证书
服务监控
前端手册 Vue 3.x
开发规范
菜单路由
Icon 图标
字典数据
系统组件
通用方法
配置读取
CRUD 组件
国际化
IDE 调试
代码格式化
前端手册 Vue 2.x
开发规范
菜单路由
Icon 图标
字典数据
系统组件
通用方法
配置读取
更新日志
【v2.1.0】开发中
【v2.0.1】2024-03-01
【v2.0.0】2024-01-26
【v1.9.0】2023-12-01
【v1.8.3】2023-10-24
yudao-cloud 开发指南
萌新必读
简介
交流群
视频教程
功能列表
快速启动(后端项目)
快速启动(前端项目)
接口文档
技术选型
项目结构
代码热加载
一键改包
删除功能
内网穿透
达梦数据库专属
微服务手册
微服务调试(必读)
注册中心 Nacos
配置中心 Nacos
服务网关 Spring Cloud Gateway
服务调用 Feign
定时任务 XXL Job
消息队列(内存)
消息队列(Redis)
消息队列(RocketMQ)
消息队列(RabbitMQ)
消息队列(Kafka)
消息队列(Cloud)
分布式事务 Seata
服务保障 Sentinel
Spring Security 专栏
Spring Security 入门
Spring Security OAuth2 入门
Spring Security OAuth2 存储器
Spring Security OAuth2 单点登录
Spring Security 常见问题
Guava 专栏
Guava 常用API汇总
本文档使用 MrDoc 发布
-
+
首页
SaaS多租户【字段隔离】
本章节,将介绍多租户的基础知识、以及怎样使用多租户的功能。 相关的视频教程: * [01、如何实现多租户的 DB 封装?(opens new window)](https://t.zsxq.com/06ufyFAeM) * [02、如何实现多租户的 Redis 封装?(opens new window)](https://t.zsxq.com/067eQfAQN) * [03、如何实现多租户的 Web 与 Security 封装?(opens new window)](https://t.zsxq.com/06Nnm6QBE) * [04、如何实现多租户的 Job 封装?(opens new window)](https://t.zsxq.com/06AYJUR3V) * [05、如何实现多租户的 MQ 与 Async 封装?(opens new window)](https://t.zsxq.com/06aq3nuNF) * [06、如何实现多租户的 AOP 与 Util 封装?(opens new window)](https://t.zsxq.com/06vFQVJIe) * [07、如何实现多租户的管理?(opens new window)](https://t.zsxq.com/063bqRrNZ) * [08、如何实现多租户的套餐?(opens new window)](https://t.zsxq.com/06rBI66yV) ## [#](https://doc.iocoder.cn/saas-tenant/#_1-%E5%A4%9A%E7%A7%9F%E6%88%B7%E6%98%AF%E4%BB%80%E4%B9%88)1. 多租户是什么? 多租户,简单来说是指**一个**业务系统,可以为**多个**组织服务,并且组织之间的数据是**隔离**的。 例如说,在服务上部署了一个 [`ruoyi-vue-pro` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro)系统,可以支持多个不同的公司使用。这里的 **一个公司就是一个租户** ,每个用户必然属于某个租户。因此,用户也只能看见自己租户下面的内容,其它租户的内容对他是不可见的。 ## [#](https://doc.iocoder.cn/saas-tenant/#_2-%E6%95%B0%E6%8D%AE%E9%9A%94%E7%A6%BB%E6%96%B9%E6%A1%88)2. 数据隔离方案 多租户的数据隔离方案,可以分成分成三种: 1. DATASOURCE 模式:独立数据库 2. SCHEMA 模式:共享数据库,独立 Schema 3. COLUMN 模式:共享数据库,共享 Schema,共享数据表 ### [#](https://doc.iocoder.cn/saas-tenant/#_2-1-datasource-%E6%A8%A1%E5%BC%8F)2.1 DATASOURCE 模式 一个租户一个数据库,这种方案的用户数据隔离级别最高,安全性最好,但成本也高。  * 优点:为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,恢复数据比较简单。 * 缺点:增大了数据库的安装数量,随之带来维护成本和购置成本的增加。 ### [#](https://doc.iocoder.cn/saas-tenant/#_2-2-schema-%E6%A8%A1%E5%BC%8F)2.2 SCHEMA 模式 多个或所有租户共享数据库,但一个租户一个表。  * 优点:为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;每个数据库可以支持更多的租户数量。 * 缺点:如果出现故障,数据恢复比较困难,因为恢复数据库将牵扯到其他租户的数据; 如果需要跨租户统计数据,存在一定困难。 ### [#](https://doc.iocoder.cn/saas-tenant/#_2-3-column-%E6%A8%A1%E5%BC%8F)2.3 COLUMN 模式 共享数据库,共享数据架构。租户共享同一个数据库、同一个表,但在表中通过 `tenant_id` 字段区分租户的数据。这是共享程度最高、隔离级别最低的模式。  * 优点:维护和购置成本最低,允许每个数据库支持的租户数量最多。 * 缺点:隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量;数据备份和恢复最困难,需要逐表逐条备份和还原。 ### [#](https://doc.iocoder.cn/saas-tenant/#_2-4-%E6%96%B9%E6%A1%88%E9%80%89%E6%8B%A9)2.4 方案选择  * 一般情况下,可以考虑采用 COLUMN 模式,开发、运维简单,以最少的服务器为最多的租户提供服务。 * 租户规模比较大,或者一些租户对安全性要求较高,可以考虑采用 DATASOURCE 模式,当然它也相对复杂的多。 * 不推荐采用 SCHEMA 模式,因为它的优点并不明显,而且它的缺点也很明显,同时对复杂 SQL 支持一般。 提问:项目支持哪些模式? 目前支持最主流的 DATASOURCE 和 COLUMN 两种模式。而 SCHEMA 模式不推荐使用,所以暂时不考虑实现。 考虑到让大家更好的理解 DATASOURCE 和 COLUMN 模式,拆成了两篇文章: * [《SaaS 多租户【字段隔离】》](https://doc.iocoder.cn/saas-tenant):讲解 COLUMN 模式 * [《SaaS 多租户【数据库隔离】》](https://doc.iocoder.cn/saas-tenant/dynamic):讲解 DATASOURCE 模式 ## [#](https://doc.iocoder.cn/saas-tenant/#_3-%E5%A4%9A%E7%A7%9F%E6%88%B7%E7%9A%84%E5%BC%80%E5%85%B3)3. 多租户的开关 系统有两个配置项,设置为 `true` 时开启多租户,设置为 `false` 时关闭多租户。 注意,两者需要保持一致,否则会报错! | 配置项 | 说明 | 配置文件 | | ----------------------------- | ---------- | -------------------------------------------------------------------------- | | `yudao.server.tenant` | 后端开关 |  | | `VUE_APP_TENANT_ENABLE` | 前端开关 |  | 疑问:为什么要设置两个配置项? 前端登录界面需要使用到多租户的配置项,从后端加载配置项的话,体验会比较差。 ## [#](https://doc.iocoder.cn/saas-tenant/#_4-%E5%A4%9A%E7%A7%9F%E6%88%B7%E7%9A%84%E4%B8%9A%E5%8A%A1%E5%8A%9F%E8%83%BD)4. 多租户的业务功能 多租户主要有两个业务功能: | 业务功能 | 说明 | 界面 | 代码 | | ---------- | ---------------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 租户管理 | 配置系统租户,创建对应的租户管理员 |  | [后端 **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java)[前端(opens new window)](https://github.com/yudaocode/yudao-ui-admin-vue2/blob/master/src/views/system/tenant/index.vue) | | 租户套餐 | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限 |  | [后端 **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java)[前端(opens new window)](https://github.com/yudaocode/yudao-ui-admin-vue2/blob/master/src/views/system/tenantPackage/index.vue) | **下面,我们来新增一个租户,它使用 COLUMN 模式。** ① 点击 [租户套餐] 菜单,点击 [新增] 按钮,填写租户的信息。  ② 点击 [确认] 按钮,完成租户的创建,它会自动创建对应的租户管理员、角色等信息。  ③ 退出系统,登录刚创建的租户。  至此,我们已经完成了租户的创建。 ## [#](https://doc.iocoder.cn/saas-tenant/#_5-%E5%A4%9A%E7%A7%9F%E6%88%B7%E7%9A%84%E6%8A%80%E6%9C%AF%E7%BB%84%E4%BB%B6)5. 多租户的技术组件 技术组件 [`yudao-spring-boot-starter-biz-tenant` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/),实现透明化的多租户能力,针对 Web、Security、DB、Redis、AOP、Job、MQ、Async 等多个层面进行封装。 ### [#](https://doc.iocoder.cn/saas-tenant/#_5-1-%E7%A7%9F%E6%88%B7%E4%B8%8A%E4%B8%8B%E6%96%87)5.1 租户上下文 [TenantContextHolder **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java) 是租户上下文,通过 ThreadLocal 实现租户编号的共享与传递。 通过调用 TenantContextHolder 的 `#getTenantId()` **静态**方法,获得当前的租户编号。绝绝绝大多数情况下,并不需要。 ### [#](https://doc.iocoder.cn/saas-tenant/#_5-2-web-%E5%B1%82%E3%80%90%E9%87%8D%E8%A6%81%E3%80%91)5.2 Web 层【重要】 > 实现可见 [`web` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web)包。 默认情况下,前端的每个请求 Header **必须**带上 `tenant-id`,值为租户编号,即 `system_tenant` 表的主键编号。  如果不带该请求头,会报“租户的请求未传递,请进行排查”错误提示。 😜 通过 `yudao.tenant.ignore-urls` 配置项,可以设置哪些 URL 无需带该请求头。例如说:  ### [#](https://doc.iocoder.cn/saas-tenant/#_5-3-security-%E5%B1%82)5.3 Security 层 > 实现可见 [`security` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security)包。 主要是校验登录的用户,校验是否有权限访问该租户,避免越权问题。 ### [#](https://doc.iocoder.cn/saas-tenant/#_5-4-db-%E5%B1%82%E3%80%90%E9%87%8D%E8%A6%81%E3%80%91)5.4 DB 层【重要】 > 实现可见 [`db` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db)包。 COLUMN 模式,基于 MyBatis Plus 自带的[多租户 **(opens new window)**](https://baomidou.com/pages/aef2f2/)功能实现。 核心:每次对数据库操作时,它会**自动**拼接 `WHERE tenant_id = ?` 条件来进行租户的过滤,并且基本支持所有的 SQL 场景。 如下是具体方式: ① **需要**开启多租户的表,必须添加 `tenant_id` 字段。例如说 `system_users`、`system_role` 等表。 ```sql CREATE TABLE `system_role` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色 ID', `name` varchar(30) CHARACTER NOT NULL COMMENT '角色名称', `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='角色信息表'; ``` 并且该表对应的 DO 需要使用到 `tenantId` 属性时,建议继承 [TenantBaseDO **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantBaseDO.java) 类。 ② **无需**开启多租户的表,需要添加表名到 `yudao.tenant.ignore-tables` 配置项目。例如说:  如果不配置的话,MyBatis Plus 会自动拼接 `WHERE tenant_id = ?` 条件,导致报 `tenant_id` 字段不存在的错误。 ### [#](https://doc.iocoder.cn/saas-tenant/#_5-5-redis-%E5%B1%82%E3%80%90%E9%87%8D%E8%A6%81%E3%80%91)5.5 Redis 层【重要】 > 实现可见 [`redis` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis)包。 由于 Redis 不同于 DB 有 `tenant_id` 字段,无法通过类似 `WHERE tenant_id` = ? 的方式过滤,所以需要通过在 Redis Key 上增加 `:t{tenantId}` 后缀的方式,进行租户之间的隔离。 例如说,假设 Redis Key 是 `user:%d`,示例是 `user:1024`;对应到多租户 1 的 Redis Key 是 `user:t1:1024`。 为什么 Redis Key 要多租户隔离呢? * ① 在使用 DATASOURCE 模式时,不同库的相同表的 id 可能相同,例如说 A 库的用户,和 B 库的用户都是 1024,直接缓存会存在 Redis Key 的冲突。 * ② 在所有模式下,跨租户可能存在相同的需要唯一的数据,例如说用户的手机号,直接缓存会存在 Redis Key 的冲突。 #### [#](https://doc.iocoder.cn/saas-tenant/#%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F%E4%B8%80-%E5%9F%BA%E4%BA%8E-spring-cache-redis%E3%80%90%E6%8E%A8%E8%8D%90%E3%80%91)使用方式一:基于 Spring Cache + Redis【推荐】 只需要一步,在方法上添加 Spring Cache 注解,例如说 `@Cachable`、`@CachePut`、`@CacheEvict`。 具体的实现原理,可见 [TenantRedisCacheManager **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java) 的源码。 注意!!!默认配置下,Spring Cache 都开启 Redis Key 的多租户隔离。如果不需要,可以将 Key 添加到 `yudao.tenant.ignore-cache` 配置项中。如下图所示:  #### [#](https://doc.iocoder.cn/saas-tenant/#%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F%E4%BA%8C-%E5%9F%BA%E4%BA%8E-redistemplate-tenantrediskeydefine)使用方式二:基于 RedisTemplate + TenantRedisKeyDefine 暂时没有合适的封装,需要在自己 format Redis Key 的时候,手动将 `:t{tenantId}` 后缀拼接上。 这也是为什么,我推荐你使用 Spring Cache + Redis 的原因! ### [#](https://doc.iocoder.cn/saas-tenant/#_5-6-aop%E3%80%90%E9%87%8D%E8%A6%81%E3%80%91)5.6 AOP【重要】 > 实现可见 [`aop` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop)包。 ① 声明 [`@TenantIgnore` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/TenantIgnore.java)注解在方法上,标记指定方法不进行租户的自动过滤,避免**自动**拼接 `WHERE tenant_id = ?` 条件等等。 例如说:[RoleServiceImpl **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java) 的 [`#initLocalCache()` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java#L83-L100)方法,加载**所有**租户的角色到内存进行缓存,如果不声明 `@TenantIgnore` 注解,会导致租户的自动过滤,只加载了某个租户的角色。 ```java // RoleServiceImpl.java public class RoleServiceImpl implements RoleService { @Resource @Lazy // 注入自己,所以延迟加载 private RoleService self; @Override @PostConstruct @TenantIgnore // 忽略自动多租户,全局初始化缓存 public void initLocalCache() { // ... 从数据库中,加载角色 } @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) public void schedulePeriodicRefresh() { self.initLocalCache(); // <x> 通过 self 引用到 Spring 代理对象 } } ``` 有一点要格外注意,由于 `@TenantIgnore` 注解是基于 Spring AOP 实现,如果是 **方法内部的调用** ,避免使用 `this` 导致不生效,可以采用上述示例的 `<x>` 处的 `self` 方式。 ② 使用 [TenantUtils **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java) 的 `#execute(Long tenantId, Runnable runnable)` 方法,模拟指定租户( `tenantId` ),执行某段业务逻辑( `runnable` )。 例如说:在 [TenantServiceImpl **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java) 的 `#createTenant(...)` 方法,在创建完租户时,需要模拟该租户,进行用户和角色的创建。如下图所示:  ### [#](https://doc.iocoder.cn/saas-tenant/#_5-7-job%E3%80%90%E9%87%8D%E8%A6%81%E3%80%91)5.7 Job【重要】 > 实现可见 [`job` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job)包。 声明 [`@TenantJob` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJob.java)注解在 Job 方法上,实现**并行**遍历每个租户,执行定时任务的逻辑。 ### [#](https://doc.iocoder.cn/saas-tenant/#_5-8-mq)5.8 MQ > 实现可见 [`mq` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq)包。 通过租户对 MQ 层面的封装,实现租户上下文,可以继续传递到 MQ 消费的逻辑中,避免丢失的问题。实现原理是: * 发送消息时,MQ 会将租户上下文的租户编号,记录到 Message 消息头 `tenant-id` 上。 * 消费消息时,MQ 会将 Message 消息头 `tenant-id`,设置到租户上下文的租户编号。 ### [#](https://doc.iocoder.cn/saas-tenant/#_5-9-async)5.9 Async > 实现可见 [`YudaoAsyncAutoConfiguration` **(opens new window)**](https://github.com/YunaiV/ruoyi-vue-pro/blob/master/yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/config/YudaoAsyncAutoConfiguration.java)类。 通过使用阿里开源的 [TransmittableThreadLocal **(opens new window)**](https://github.com/alibaba/transmittable-thread-local) 组件,实现 Spring Async 执行异步逻辑时,租户上下文可以继续传递,避免丢失的问题。 ## [#](https://doc.iocoder.cn/saas-tenant/#_6-%E7%A7%9F%E6%88%B7%E7%8B%AC%E7%AB%8B%E5%9F%9F%E5%90%8D)6. 租户独立域名 在我们使用 SaaS 云产品的时候,每个租户会拥有 **独立的子域名** ,例如说:租户 A 对应 `a.iocoder.cn`,租户 B 对应 `b.iocoder.cn`。 目前管理后台已经提供类似的能力,更多大家可以基于它去拓展。实现方式: 1. 在 `system_tenant` 表里,有个 `website` 字段为该租户的独立域名,你可以填写你希望分配给它的子域名。 2. 在 Nginx 上做 **泛域名解析** 到你的前端项目,例如说 Nginx 的 `server_name` `*.iocoder.cn` 解析到 Vue3 管理后台。 这样用户在访问管理后台的登录界面,会自动根据当前访问域名的 `host`,向后端获得对应的 `tenant-id` 编号,后续请求都带上它! ps:商城 uniapp 暂时还没做,感兴趣可以 pull request 贡献下噢!
LazzMan
2024年4月25日 19:53
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码