一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第27天,点击查看活动详情
简介
多租户简单来说是指一个单独的实例可以为多个组织服务。多租户技术为共用的数据中心内如何以单一系统架构与服务提供多数客户端相同甚至可定制化的服务,并且仍然可以保障客户的数据隔离。
实现技术方案
详细的列举了实现多租户的技术方案以及每种方案的优缺点。
Mybatis-plus实现多租户的技术方案选择的是第三种方案共享数据库和共享数据,下面讲解下Mybatis-plus如何实现多租户。
多租实现
自定义定义多租户处理规则
public class MyTenantLineHandler implements TenantLineHandler
{
//返回租户id的值
@Override
public Expression getTenantId()
{
//从容器中获取租户Id
Long id=TenantIdManager.getCurrentTenantId();
if(id==null)
{
return null;
}
LongValue longValue =new LongValue(id);
return longValue;
}
//租户id字段名称
@Override
public String getTenantIdColumn()
{
return "tenantId";
}
//忽略此表
@Override
public boolean ignoreTable(String tableName)
{
List<String> list =new ArrayList<String>();
//list.add("t_user_1");
if(list.contains(tableName)){
return true;
}
return false;
}
//如何已经包含此字段,忽略该字段插入
public boolean ignoreInsert(List<Column> columns, String tenantIdColumn)
{
return columns.stream().map(Column::getColumnName).anyMatch(i -> i.equalsIgnoreCase(tenantIdColumn));
}
}
实体添加租户ID字段
/**
* 租户id
*/
@TableField(value="tenantId")
private Long tenantId;
租户ID管理器
public class TenantIdManager
{
/** 当前用户租户 KEY */
private static final String KEY_CURRENT_TENANT_ID = "KEY_CURRENT_PROVIDER_ID";
/** 保存当前租户ID */
private static final Map<String, Object> TENANT_MAP = new ConcurrentHashMap<>();
/**
* 设置租户
* @param tenantId 租户ID
*/
public static void setCurrentTenantId(Long tenantId) {
TENANT_MAP.put(KEY_CURRENT_TENANT_ID, tenantId);
}
/**
* 返回当前用户租户ID
* @return
*/
public static Long getCurrentTenantId() {
return (Long) TENANT_MAP.get(KEY_CURRENT_TENANT_ID);
}
}
添加多租户拦截器
@Bean
public MybatisPlusInterceptor paginationInterceptor()
{
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//多租户
interceptor.addInnerInterceptor(tenantLineInnerInterceptor());
}
@Bean
public TenantLineInnerInterceptor tenantLineInnerInterceptor ()
{
TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor();
tenantInterceptor.setTenantLineHandler(new MyTenantLineHandler());
return tenantInterceptor;
}
示例
@RequestMapping("/addUser")
public String addUser(@RequestParam Long tenantId)
{
TenantIdManager.setCurrentTenantId(tenantId);
TUser tUser =new TUser();
tUser.setName("tenantid1");
tUser.setAge(28);
tUser.setAddress("SZ");
userService.save(tUser);
return "成功";
}
测试结果
==> Preparing: INSERT INTO t_user_1 (name, age, address, deleteFlag, createUser, createDate, version, tenantId) VALUES (?, ?, ?, ?, ?, ?, ?, 3)
==> Parameters: tenantid1(String), 28(Integer), SZ(String), false(Boolean), jiansheng(String), 2022-04-28 18:23:00.504(Timestamp), 0(Integer)
<== Updates: 1
从结果上可以看出SQL语句中会自动插入租户ID字段的值。目前支持查询、新增、修改、删除都会自动给拼接租户ID字段。
特殊场景
对相关表进行忽略租户ID
如果相关表需要忽略租户ID,我们只需要实重写TenantLineHandler 中的ignoreTable方法,将相关的表进行忽略
public boolean ignoreTable(String tableName)
{
List<String> list =new ArrayList<String>();
list.add("t_user_1");
if(list.contains(tableName)){
return true;
}
return false;
}
针对某些方法进行忽略
针对某些方法进行忽略,需要在Mapper的定义方法中添加InterceptorIgnore注解且设置tenantLine为On即可。
@InterceptorIgnore(illegalSql = "1",tenantLine = "on")
List<TUser> getUserList();
如果需要是整个mapper类的所有方法都不生效,则需要将注解配置在Mapper类上
@InterceptorIgnore(illegalSql = "1",tenantLine = "on")
public interface UserMapper extends SuperMapper<TUser>
{
}
总结
虽然Mybatis-Plus提供多租户实现,但是我们需要注意如下事项:
-
项目一旦启用多租户的配置后,所有执行的 SQL都会进行相关的处理,存在一定的风险,需要在项目初期对相关的表进行分析和排查。
-
项目中需要规范SQl语句,Sql涉及到多个表的每个表都要给别名,特别是 inner join的要写标准格式。
-
目前Mybatis-Plus对于多租户的SQL解析,针对一些特殊的函数目前无法自动拼接租户字段。
-
多租户不等于数据权限,需要进行区分。
多租户技术是一种架构设计,企业需要根据情况选择合适的方案。项目初期需要我们就需要考虑设计,越往后期修改的成本越高。