数据逻辑隔离
1、需求
对已有服务进行逻辑隔离,按照请求头中租户标志进行区分,采用逻辑隔离方式,在对应表中增加租户字段,执行新增、查询、删除sql语句时携带该字段
2、方案
采用mybatis框架自带的TenantLineInnerInterceptor插件,
可以实现插入、查询时自动携带租户字段,并且支持灵活配置
步骤一
yml租户配置
tenant:
# 是否开启租户模式
enable: true
# 需要排除的多租户的表
exclusionTable:
- "t_test_log"
- "t_test_user"
# 租户字段名称
column: tenant_id
步骤二
除了已排除的表,其余表增加租户标志
步骤三
租户配置
@Data
@Configuration
@ConfigurationProperties(prefix = "tenant")
public class TenantProperties {
/**
* 是否开启租户模式
*/
private Boolean enable;
/**
* 多租户字段名称
*/
private String column;
/**
* 需要排除的多租户的表
*/
private List<String> exclusionTable;
}
租户插件配置
@Configuration
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@AutoConfigureBefore(MybatisPlusConfig.class)
public class TenantConfig {
private final TenantProperties tenantProperties;
/**
* 新多租户插件配置,一缓和二缓遵循mybatis的规则,
* 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
* 避免缓存万一出现问题
*
* @return TenantLineInnerInterceptor
*/
@Bean
public TenantLineInnerInterceptor tenantLineInnerInterceptor() {
return new TenantLineInnerInterceptor(new TenantLineHandler() {
/**
* 获取租户ID
* @return Expression
*/
@Override
public Expression getTenantId() {
String tenant = TenementFlagContextHolder.getTenementFlag();
if (tenant != null) {
return new StringValue(TenementFlagContextHolder.getTenementFlag());
}
return new NullValue();
}
/**
* 获取多租户的字段名
* @return String
*/
@Override
public String getTenantIdColumn() {
return tenantProperties.getColumn();
}
/**
* 过滤不需要根据租户隔离的表
* 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
* @param tableName 表名
*/
@Override
public boolean ignoreTable(String tableName) {
return tenantProperties.getExclusionTable().stream().anyMatch(
(t) -> t.equalsIgnoreCase(tableName)
);
}
});
}
}
mybatis配置,根据配置开启租户插件
@Configuration
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@MapperScan({"com.taoge.**"})
public class MybatisPlusConfig {
private final TenantLineInnerInterceptor tenantLineInnerInterceptor;
private final TenantProperties tenantProperties;
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false
* 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//多租户插件
if (tenantProperties.getEnable()) {
interceptor.addInnerInterceptor(tenantLineInnerInterceptor);
}
//分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//防止全表更新与删除插件: BlockAttackInnerInterceptor
BlockAttackInnerInterceptor blockAttackInnerInterceptor = new BlockAttackInnerInterceptor();
interceptor.addInnerInterceptor(blockAttackInnerInterceptor);
return interceptor;
}
/**
* 乐观锁插件 当要更新一条记录的时候,希望这条记录没有被别人更新
* https://mybatis.plus/guide/interceptor-optimistic-locker.html#optimisticlockerinnerinterceptor
*/
@Bean
public OptimisticLockerInnerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInnerInterceptor();
}
}