mybatisplus多租户原理略解

概述

当前mybatisPlus版本

<dependency>
    <groupId>com.baomidougroupId>
    <artifactId>mybatis-plus-boot-starterartifactId>
    <version>3.5.3.2version>
dependency>

jdk版本:17
springboot版本:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-dependenciesartifactId>
    <version>3.1.2version>
dependency>

业务中想按照租户划分权限时,一般简单的就是在表里加个字段,但是这样每个sql语句都要改造,很不方便。

mybatisplus有个现成的租户插件
com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor
在配置租户插件之前,需要在token中先塞入租户id,方便后面在拦截器中获取当前用户的租户.

租户注入的关键代码在
com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor#buildTableExpression
mybatisplus多租户原理略解_第1张图片

配置

配置的时候租户拦截器需要放在第一个,即`index=0

package org.qps.common.tenant.handle;

import cn.hutool.core.collection.ListUtil;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import org.qps.common.core.utils.StringUtils;
import org.qps.common.satoken.utils.LoginHelper;
import org.qps.common.tenant.helper.TenantHelper;
import org.qps.common.tenant.properties.TenantProperties;
import lombok.AllArgsConstructor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.StringValue;

import java.util.List;

/**
 * 自定义租户处理器
 *
 */
@AllArgsConstructor
public class PlusTenantLineHandler implements TenantLineHandler {

    private final TenantProperties tenantProperties;

    @Override
    public Expression getTenantId() {
        String tenantId = LoginHelper.getTenantId();
        if (StringUtils.isBlank(tenantId)) {
            return new NullValue();
        }
        String dynamicTenantId = TenantHelper.getDynamic();
        if (StringUtils.isNotBlank(dynamicTenantId)) {
            // 返回动态租户
            return new StringValue(dynamicTenantId);
        }
        // 返回固定租户
        return new StringValue(tenantId);
    }

    @Override
    public boolean ignoreTable(String tableName) {
        String tenantId = LoginHelper.getTenantId();
        // 判断是否有租户
        if (StringUtils.isNotBlank(tenantId)) {
            // 不需要过滤租户的表
            List<String> excludes = tenantProperties.getExcludes();
            // 非业务表
            List<String> tables = ListUtil.toList(
                "gen_table",
                "gen_table_column"
            );
            tables.addAll(excludes);
            return tables.contains(tableName);
        }
        return true;
    }

}









package org.qps.common.tenant.config;

import cn.dev33.satoken.dao.SaTokenDao;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.qps.common.core.utils.reflect.ReflectUtils;
import org.qps.common.mybatis.config.MybatisPlusConfig;
import org.qps.common.redis.config.RedisConfig;
import org.qps.common.redis.config.properties.RedissonProperties;
import org.qps.common.tenant.core.TenantSaTokenDao;
import org.qps.common.tenant.handle.PlusTenantLineHandler;
import org.qps.common.tenant.handle.TenantKeyPrefixHandler;
import org.qps.common.tenant.manager.TenantSpringCacheManager;
import org.qps.common.tenant.properties.TenantProperties;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.SingleServerConfig;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;

import java.util.ArrayList;
import java.util.List;

/**
 * 租户配置类
 *
 * TenantProperties 自定义租户配置对象
 * MybatisPlusConfig 自定义mybatisPlus配置,维护了分页拦截器等基础拦截器
 */
@EnableConfigurationProperties(TenantProperties.class)
@AutoConfiguration(after = {MybatisPlusConfig.class})
@ConditionalOnProperty(value = "tenant.enable", havingValue = "true")
public class TenantConfig {

    /**
     * 初始化租户配置
     */
    @Bean
    public boolean tenantInit(MybatisPlusInterceptor mybatisPlusInterceptor,
                              TenantProperties tenantProperties) {
        List<InnerInterceptor> interceptors = new ArrayList<>();
        // 多租户插件 必须放到第一位
        interceptors.add(tenantLineInnerInterceptor(tenantProperties));
        interceptors.addAll(mybatisPlusInterceptor.getInterceptors());
        mybatisPlusInterceptor.setInterceptors(interceptors);
        return true;
    }

    /**
     * 多租户插件
     */
    public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) {
        return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties));
    }

}

多租户拦截忽略

启用多租户之后,有些业务sql想查询多个租户的数据或者不想被注入租户id。

那么可以加上com.baomidou.mybatisplus.annotation.InterceptorIgnore
@InterceptorIgnore(tenantLine = "true")
即可忽略

原理可以参考源码
com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor#beforeQuery

com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper#willIgnoreTenantLine

com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper#willIgnore

将注解数据初始化到内存中
com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper#initSqlParserInfoCache(java.lang.Class)

你可能感兴趣的:(mybatis,springboot,mybatis)