如何添加“默认给Sql查询语句加上租户条件”的功能

从零实现"默认给 SQL 查询语句加上租户条件 "的功能,本质上是利用 MyBatis Plus 的插件机制配合 ThreadLocal 上下文来实现的。

我们需要构建一条完整的 "数据 -> 规则 -> 执行" 的链路。以下是标准化的 5 步实现指南


第一步:准备"背包" (定义上下文容器)

你需要一个地方在当前线程中存储"当前租户是谁"。

代码核心: 利用 ThreadLocal

java 复制代码
public class TenantContextHolder {
    // 存放当前租户ID
    private static final ThreadLocal<Long> TENANT_ID = new TransmittableThreadLocal<>();

    public static void setTenantId(Long tenantId) {
        TENANT_ID.set(tenantId);
    }

    public static Long getTenantId() {
        return TENANT_ID.get();
    }

    public static void clear() {
        TENANT_ID.remove();
    }
}

第二步:制定"规则" (实现 Handler 接口)

你需要告诉 MyBatis Plus 具体的过滤逻辑:租户ID是多少?列名叫什么?哪些表不需要加?

代码核心: 实现 TenantLineHandler 接口。

java 复制代码
@Component
public class MyTenantLineHandler implements TenantLineHandler {

    // 1. 告诉 MP,当前租户ID是多少 (从背包里拿)
    @Override
    public Expression getTenantId() {
        Long tenantId = TenantContextHolder.getTenantId();
        // 如果没拿到ID(比如没登录),返回 NullValue 可能会导致报错或查不到数据
        // 通常这里会做判空或者返回默认值
        return new LongValue(tenantId);
    }

    // 2. 告诉 MP,数据库里租户列的名字叫什么
    @Override
    public String getTenantIdColumn() {
        return "tenant_id";
    }

    // 3. 告诉 MP,哪些表需要忽略 (白名单)
    @Override
    public boolean ignoreTable(String tableName) {
        // A. 全局白名单:系统表(字典、菜单)不需要隔离
        if ("sys_dict".equals(tableName) || "sys_menu".equals(tableName)) {
            return true;
        }
        // B. 动态白名单:配合 @TenantIgnore 注解使用
        if (TenantContextHolder.isIgnore()) {
            return true;
        }
        // 默认:必须加过滤条件
        return false;
    }
}

第三步:组装"引擎" (配置 MyBatis 拦截器)

有了规则(Handler),你需要把它交给执行者(Interceptor),并把执行者放入 MyBatis 的插件链中。

代码核心: 配置 MybatisPlusInterceptor Bean。

java 复制代码
@Configuration
public class MybatisConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(MyTenantLineHandler tenantLineHandler) {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 核心动作:创建租户拦截器,并注入上面的规则 Handler
        // ⚠️注意:建议放在分页插件之前
        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(tenantLineHandler));

        // 添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

        return interceptor;
    }
}

第四步:数据"注入" (配置 Web 过滤器)

引擎装好了,但还得有人把燃料(租户ID)塞进第一步的"背包"里。通常是在请求刚进来时处理。

代码核心: 实现 FilterHandlerInterceptor

java 复制代码
@Component
public class TenantContextFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        try {
            // 1. 从请求头 Header 获取 tenant-id
            HttpServletRequest req = (HttpServletRequest) request;
            String tenantIdStr = req.getHeader("tenant-id");
            
            if (tenantIdStr != null) {
                // 2. 塞进 ThreadLocal 背包
                TenantContextHolder.setTenantId(Long.valueOf(tenantIdStr));
            }
            
            // 3. 放行,执行后续业务逻辑 (Service -> Mapper -> SQL拦截器)
            chain.doFilter(request, response);
            
        } finally {
            // 4. 【重要】请求结束,清空背包,防止线程污染
            TenantContextHolder.clear();
        }
    }
}

🌟 总结:这一套下来发生了什么?

  1. 请求进来 :Filter 从 Header 拿到 tenant_id=1,存入 TenantContextHolder
  2. 业务查询 :你写了 userMapper.selectList(null)
  3. 拦截改写TenantLineInnerInterceptor 拦截 SQL,调用 MyTenantLineHandler
  4. 读取规则 :Handler 从 TenantContextHolder 拿到 1
  5. SQL 变身 :SQL 被自动拼接为 SELECT * FROM user WHERE tenant_id = 1
  6. 请求结束 :Filter 清空 TenantContextHolder

这就是实现"全自动多租户隔离"的完整标准流程。

相关推荐
马克Markorg5 小时前
常见的向量数据库和具有向量数据库能力的数据库
数据库
冷雨夜中漫步5 小时前
Python快速入门(6)——for/if/while语句
开发语言·经验分享·笔记·python
郝学胜-神的一滴5 小时前
深入解析Python字典的继承关系:从abc模块看设计之美
网络·数据结构·python·程序人生
百锦再5 小时前
Reactive编程入门:Project Reactor 深度指南
前端·javascript·python·react.js·django·前端框架·reactjs
喵手7 小时前
Python爬虫实战:旅游数据采集实战 - 携程&去哪儿酒店机票价格监控完整方案(附CSV导出 + SQLite持久化存储)!
爬虫·python·爬虫实战·零基础python爬虫教学·采集结果csv导出·旅游数据采集·携程/去哪儿酒店机票价格监控
Coder_Boy_7 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
2501_944934737 小时前
高职大数据技术专业,CDA和Python认证优先考哪个?
大数据·开发语言·python
helloworldandy7 小时前
使用Pandas进行数据分析:从数据清洗到可视化
jvm·数据库·python
肖永威9 小时前
macOS环境安装/卸载python实践笔记
笔记·python·macos
TechWJ9 小时前
PyPTO编程范式深度解读:让NPU开发像写Python一样简单
开发语言·python·cann·pypto