目标:实现同一个mysql实例下不同库相同表结构的表数据隔离处理
此处实现场景:请求中多租户Id 【tenant-id】值即为库名值,也为表中 tenant_id 多租户列值
基于
多租户实现 基于SpringBoot+MybtaisPlus ~ 行级别隔离实现_0110编程之路的博客-CSDN博客
扩展实现
1、Starter 封装 ~调整
①、propertis 增加 isolationRow isolationTable 表级别、行级别控制字段
package com.ap.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* 类名:MultiTenantProperties.java
* 描述:多租户配置属性类
*
* @author AP
* @version 1.0
* @date 2023/7/12 10:02
*/
@ConfigurationProperties(prefix = MultiTenantProperties.PREFIX)
public class MultiTenantProperties {
public static final String PREFIX = "multi-tenant";
/**
* 是否启用
*/
private boolean enabled;
/**
* 租户id列名称
*/
private String tenantIdColumn;
/**
* 忽略多租户限制条件的库表名
*/
private List<String> ignoreTables;
/**
* request請求header 中的多租户标识
*/
private String headerTenantId;
/**
* 基于字段tenantIdColumn的行级别隔离
*/
private boolean isolationRow;
/**
* 基于Schema.表名的表级别隔离
*/
private boolean isolationTable;
public boolean getIsolationRow() {
return isolationRow;
}
public void setIsolationRow(boolean isolationRow) {
this.isolationRow = isolationRow;
}
public boolean getIsolationTable() {
return isolationTable;
}
public void setIsolationTable(boolean isolationTable) {
this.isolationTable = isolationTable;
}
public String getHeaderTenantId() {
return headerTenantId;
}
public void setHeaderTenantId(String headerTenantId) {
this.headerTenantId = headerTenantId;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getTenantIdColumn() {
return tenantIdColumn;
}
public void setTenantIdColumn(String tenantIdColumn) {
this.tenantIdColumn = tenantIdColumn;
}
public List<String> getIgnoreTables() {
return ignoreTables;
}
public void setIgnoreTables(List<String> ignoreTables) {
this.ignoreTables = ignoreTables;
}
}
②、AutoConfiguration配置调整
package com.ap.config;
/**
* 类名:MultiTenantAutoConfiguration.java
* 描述: 多租户自动配置处理类
*
* @author AP
* @version 1.0
* @date 2023/7/12 10:12
*/
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.troila.context.TenantContext;
import com.troila.properties.MultiTenantProperties;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
@Configuration
@EnableConfigurationProperties(MultiTenantProperties.class)
@ConditionalOnProperty(prefix = MultiTenantProperties.PREFIX, value = "enabled", havingValue = "true")
public class MultiTenantAutoConfiguration {
@Resource
MybatisPlusInterceptor mybatisPlusInterceptor;
@Resource(type=MultiTenantProperties.class)
MultiTenantProperties properties;
@PostConstruct
public void tenantLineInnerInterceptor(){
// 多租户~字段拦截器
if (properties.getIsolationRow()) {
mybatisPlusInterceptor.addInnerInterceptor(
new TenantLineInnerInterceptor(
new TenantLineHandler() {
@Override
public Expression getTenantId() {
return new StringValue(TenantContext.getRequiredTenantId());
}
// 租户id 列名值
@Override
public String getTenantIdColumn(){
return properties.getTenantIdColumn();
}
// 返回 false 表示所有表都需要拼多租户条件
@Override
public boolean ignoreTable(String tableName) {
List<String> ignoreTables = properties.getIgnoreTables();
return !CollectionUtils.isEmpty(ignoreTables) && ignoreTables.contains(tableName);
}
}
)
);
}
// 多租户~动态名拦截器
if (properties.getIsolationTable()) {
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName) -> {
// 获取参数方法
String tenantId = TenantContext.getRequiredTenantId();
List<String> ignoreTables = properties.getIgnoreTables();
// 忽略表处理
if(!CollectionUtils.isEmpty(ignoreTables) && ignoreTables.contains(tableName)){
return tableName;
}
return tenantId.concat(".").concat(tableName);
});
mybatisPlusInterceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
}
}
}
2、Starter使用 ~调整
①、yml 配置
# 多租户
multi-tenant:
# 是否开启多租户处理 true 开启 false 关闭
enabled: true
# 表对应的租户字段名称
tenant-id-column: "tenant_id"
# 请求头中租户key值
header-tenant-id: "tenant-id"
# Schema table隔离 true 开启 false 关闭
isolation-table: true
# Row 隔离 true 开启 false 关闭
isolation-row: true
# 忽略处理表
ignore-tables:
- message
- user
②、忽略插件处理
支持注解在 Mapper 上以及 Mapper.Method 上 同时存在则 Mapper.method 比 Mapper 优先级高
支持: true 和 false , 1 和 0 , on 和 off
各属性返回 true 表示不走插件(在配置了插件的情况下,不填则默认表示 false)
@InterceptorIgnore(tenantLine = "1", dynamicTableName = "1")