一、插件TenantLineInnerInterceptor实现多租户
参考:
【精选】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";
}