上篇文章中已经介绍了实现mybatisPlus多租户的最简单的方式,但逻辑也比较单一,只有多租户的基础功能
这个多租户plus版本在原来的基础功能上做了一些功能的集成
1.通过配置来控制多租户功能的启用
2.通过配置来指定多租户字段
3.通过配置来排除需要走多租户逻辑的表
4.通过扫描数据库表来判断是否需要走多租户逻辑的表
5.通过自定义注解、切面、上下文来实现局部排除多租户逻辑
但是这个版本仍然不能做更多的适配项目的多租户逻辑,例如使用多个字段来实现多租户数据隔离
后续会再写一个可以实现多个字段控制的多租户版本
多租户配置类properties类,TenantProperties
package com.zzh.manage.tenantPlus;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
@Data
@ConfigurationProperties(prefix = "mybatis-plus.tenant")
public class TenantProperties {
/**
* 控制是否开启多租户功能的开关
*/
private Boolean enabled;
/**
* 多租户字段
*/
private String tenantColumn;
/**
* 需要排除的多租户的表
*/
private List<String> excludesTableName;
}
自定义多租户处理器TenantHandler
package com.zzh.manage.tenantPlus;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.schema.Column;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
public class TenantHandler implements TenantLineHandler, InitializingBean {
/**
* 扫描出来的数据库种包含多租户字段的表
*/
private List<String> tableNames;
private static final String TENANT_ID = "tenant_Id";
/**
* 扫描数据库表字段的sql语句
*/
private static final String SQL_PREFIX = "select distinct table_name from information_schema.columns where column_name = '%s'";
/**
* 配置项
*/
private final TenantProperties properties;
/**
* jdbcTemplate,用jdbcTemplate是因为不会走mybatis拦截器
*/
private final JdbcTemplate jdbcTemplate;
public TenantHandler(TenantProperties properties, JdbcTemplate jdbcTemplate) {
this.properties = properties;
this.jdbcTemplate = jdbcTemplate;
}
/**
* 获取租户 ID 值表达式,只支持单个 ID 值
* <p>
*
* @return 租户 ID 值表达式
*/
@Override
public Expression getTenantId() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String id = request.getHeader("tenantId");
return new StringValue(StringUtils.isNotBlank(id) ? id : "empty");
}
/**
* 获取租户字段名
* <p>
* 默认字段名叫: tenant_id
*
* @return 租户字段名
*/
@Override
public String getTenantIdColumn() {
return Objects.nonNull(properties) && StringUtils.isNotBlank(properties.getTenantColumn()) ? properties.getTenantColumn() : TENANT_ID;
}
/**
* 根据表名判断是否忽略拼接多租户条件
* <p>
* 默认都要进行解析并拼接多租户条件
*
* @param tableName 表名
* @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件
*/
@Override
public boolean ignoreTable(String tableName) {
if (Objects.nonNull(TenantContext.getTenantContext()) && TenantContext.getTenantContext()) {
return false;
}
if (Objects.nonNull(properties)
&& CollectionUtils.isNotEmpty(properties.getExcludesTableName())
&& properties.getExcludesTableName().contains(tableName)) {
return false;
}
return CollectionUtils.isNotEmpty(tableNames) && tableNames.contains(tableName);
}
/**
* 忽略插入租户字段逻辑
*
* @param columns 插入字段
* @param tenantIdColumn 租户 ID 字段
* @return
*/
@Override
public boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {
return columns.stream().map(Column::getColumnName).anyMatch(i -> i.equalsIgnoreCase(tenantIdColumn));
}
/**
* 多租户表初始化
*
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
String column = Objects.nonNull(properties) && StringUtils.isNotBlank(properties.getTenantColumn()) ? properties.getTenantColumn() : TENANT_ID;
tableNames = jdbcTemplate.queryForList(String.format(SQL_PREFIX, column), String.class);
}
}
多租户配置类TenantConfig
package com.zzh.manage.tenantPlus;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* 多租户配置类,根据配置文件来判断是否加载多租户配置类
*/
@Configuration
@EnableConfigurationProperties({TenantProperties.class})
@ConditionalOnProperty(prefix = "mybatis-plus.tenant", name = "enable", havingValue = "true")
public class TenantConfig {
private final TenantProperties properties;
public TenantConfig(TenantProperties properties) {
this.properties = properties;
}
/**
* 注册多租户拦截器
*
* @param jdbcTemplate
* @return
*/
@Bean
public MybatisPlusInterceptor interceptor(@Autowired JdbcTemplate jdbcTemplate) {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(getTenantHandler(jdbcTemplate, properties)));
return interceptor;
}
/**
* 注册多租户处理器
*
* @param jdbcTemplate
* @param properties
* @return
*/
@Bean
public TenantHandler getTenantHandler(JdbcTemplate jdbcTemplate, TenantProperties properties) {
return new TenantHandler(properties, jdbcTemplate);
}
}
自定义注解Tenant
package com.zzh.manage.tenantPlus;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解Tenant
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Tenant {
/**
* true:不执行多租户逻辑,false:执行多租户逻辑
* @return
*/
boolean isIgnore();
}
多租户上下文TenantContext
package com.zzh.manage.tenantPlus;
/**
* 多租户控制器上下文
*/
public class TenantContext {
private static final ThreadLocal<Boolean> TENANT_CONTEXT = new ThreadLocal<>();
/**
* 设置上下文的值
* @param tenant
*/
public static void setTenantContext(Boolean tenant) {TENANT_CONTEXT.set(tenant);}
/**
* 获取上下文的值
* @return
*/
public static Boolean getTenantContext() {return TENANT_CONTEXT.get();}
/**
* 移除上下文的值
*/
public static void removeTenantContext() {TENANT_CONTEXT.remove();}
}
多租户Tenant切面
package com.zzh.manage.tenantPlus;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* 多租户切面
*/
@Aspect
@Component
public class TenantAspect {
/**
* 切面
* 拦截类上和方法上的Tenant注解
*/
@Pointcut("@annotation(com.zzh.manage.tenantPlus.Tenant) || @within(com.zzh.manage.tenantPlus.Tenant)")
public void pointcut() {
}
/**
* 判断是否需要为多租户上下文赋值
* @param point
* @return
* @throws Throwable
*/
@Around(value = "pointcut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
try {
Method method = ((MethodSignature) point.getSignature()).getMethod();
Tenant tenant = method.getAnnotation(Tenant.class);
if (Objects.isNull(tenant)) {
tenant = point.getClass().getAnnotation(Tenant.class);
}
if (Objects.nonNull(tenant)) {
TenantContext.setTenantContext(tenant.isIgnore());
}
return point.proceed();
} finally {
TenantContext.removeTenantContext();
}
}
}
多租户配置application.yml
server:
port: 8080
spring:
application:
name: manage
mybatis-plus:
tenant:
enabled: true #多租户开关
tenant-column: tenant_id #多租户字段
excludes-table-name: #需要排除多租户的表
- t_table_name1
- t_table_name2
- t_table_name3
标签:return,租户,properties,Plus,org,Mybatis,import,public
From: https://blog.csdn.net/weixin_45665996/article/details/144727609