一、插件TenantLineInnerInterceptor实现多租户

参考:

多租户插件 | MyBatis-Plus

【精选】Mybatis Plus 多租户id使用-CSDN博客

二、保存报错

原先的功能接口都无法保存,出现如下错误:net.sf.jsqlparser.statement.select.SetOperationList cannot be cast to net.sf.jsqlparser.statement.select.PlainSelect

 解决方案:

进行依赖排除处理jsqlparser、mybatis、mybatis-spring,重新运行项目服务后正常

<!--mybatis-->
	<dependency>
	    <groupId>com.baomidou</groupId>
	    <artifactId>mybatis-plus-boot-starter</artifactId>
	    <version>3.5.3.1</version>
	</dependency>
	<!-- pagehelper 分页封装使用 -->
	<!-- pagehelper 依赖升级版本1.4.6并且排除jsqlparser依赖,使pagehelper分页与mybatis-plus分页兼容存在 -->
	<dependency>
		<groupId>com.github.pagehelper</groupId>
		<artifactId>pagehelper-spring-boot-starter</artifactId>
		<version>1.4.6</version>
		<exclusions>
			<exclusion>
				<groupId>org.mybatis</groupId>
				<artifactId>mybatis</artifactId>
			</exclusion>
			<exclusion>
				<groupId>org.mybatis</groupId>
				<artifactId>mybatis-spring</artifactId>
			</exclusion>
			<exclusion>
				<groupId>com.github.jsqlparser</groupId>
				<artifactId>jsqlparser</artifactId>
			</exclusion>
		</exclusions>
	</dependency>

参考:【java】使pagehelper分页与mybatis-plus分页兼容存在 - 掘金

三、sql中存在inner join报错TENANT_ID字段不存在

解决方案:   

对应mapper方法加了@InterceptorIgnore(tenantLine = "true")也无效,于是将inner join改为left join解决。

官网说明

四、oracle版本模糊查询用“||“拼接报错

Caused by: net.sf.jsqlparser.parser.ParseException: Encountered unexpected token: "||" <OP CONCAT>
at ine 4. column 47
Was expecting one of:
q
AA
0s
"COLLATE
"CONNECT"EMIT“
"ESCAPE"
"EXCEPT
"GROUP"
"HAVING""INTERSECT“MINUS"
"START""UNION"
net,sf,isalparser.parser,cciSalParserateParseException(CCJSqlParser.java:31234)

 解决方案:

用oracle的concat()函数代替"||"拼接。

五、解决自动任务和超级管理员不隔离数据问题

通过实现TenantLineHandler的ignoreTable方法解决。

1、TenantProperties.java用于系统读取配置文件

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.List;

/**
 * 白名单配置
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "tenant")
public class TenantProperties {

    /**
     * 是否开启租户模式
     */
    private Boolean enable;

    /**
     * 多租户字段名称
     */
    private String column;

    /**
     * 需要排除的多租户的表
     */
    private List<String> exclusionTable;

    /**
     * 需要排除的租户
     */
    private List<String> exclusionTenant;

}

2、.yml文件配置

tenant:
  # 是否开启租户模式
  enable: true
  # 需要排除的租户  NO_LOGIN用于处理定时任务不隔离租户;给admin分配SUPER_ADMIN租户号,SUPER_ADMIN用于处理超级用户查询全部租户数据
  exclusionTenant:
    - "SUPER_ADMIN"
    - "NO_LOGIN"
  # 需要排除的多租户的表
  exclusionTable:
    - "sys_config"
    - "sys_dict_data"
    - "sys_dict_type"
    - "sys_job"
    - "sys_job_log"
    - "sys_user_role"
    - "sys_role_dept"
    - "sys_role"
    - "sys_logininfor"
    - "sys_menu"
    - "sys_role_menu"
    - "sys_oper_log"
  # 租户字段名称
  column: tenant_id

3、实现TenantLineHandler

需要将mybatisPlusInterceptor加入插件中

sessionFactory.setPlugins(mybatisPlusInterceptor());

    @Autowired
    private TenantProperties tenantProperties;

 @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
            @Override
            public Expression getTenantId() {
                String tenant = SecurityUtils.getUserTenantId();
                if (tenant != null) {
                    return new StringValue(tenant);
                }
                return new NullValue();
                //获得当前登录用户的租户id
//                return new LongValue(1111);
            }

            @Override
            public String getTenantIdColumn() {
                // 指定对应数据库表中存储租户ID的字段名
                return tenantProperties.getColumn();
            }

            /**
             * 过滤不需要根据租户隔离的表
             * 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
             * @param tableName 表名
             */
            @Override
            public boolean ignoreTable(String tableName) {
                String tenant = SecurityUtils.getUserTenantId();

                // 不隔离的租户
                Map<String, String> collect = tenantProperties.getExclusionTenant().stream().collect(Collectors.toMap(a -> a, o -> o));
                if(collect.containsKey(tenant)){
                    return true;
                }
                //不隔离的表
                return tenantProperties.getExclusionTable().stream().anyMatch(
                        (t) -> t.equalsIgnoreCase(tableName)
                );
            }
        }));
        return interceptor;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
    {
        String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage");
        String mapperLocations = env.getProperty("mybatis-plus.mapperLocations");
        String configLocation = env.getProperty("mybatis-plus.configLocation");
        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
        VFS.addImplClass(SpringBootVFS.class);

        //final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        MybatisSqlSessionFactoryBean sessionFactory=new MybatisSqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
        sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
        // 多租户开关
        if(tenantProperties.getEnable()){
            sessionFactory.setPlugins(mybatisPlusInterceptor());
        }
        return sessionFactory.getObject();
    }

4、在SecurityUtils中新增获取租户号方法

    /**
     * 获取租户ID
     **/
    public static String getUserTenantId()
    {
        try{
            LoginUser loginUser = getLoginUser();
            if(loginUser != null){
                String tenantId = getLoginUser().getUser().getTenantId();
                if(tenantId == null){
                    throw new ServiceException("获取租户ID异常", HttpStatus.UNAUTHORIZED);
                }else {
                    return tenantId;
                }
            }else{
                return "NO_LOGIN";
            }
        }catch (Exception e){

        }
        return "NO_LOGIN";
    }

Logo

快速构建 Web 应用程序

更多推荐