MybatisPlus进阶篇学习笔记(六)------多租户

原创:兜里的猫

本章目录

  • 多租户简介
  • 多租户实现
  • 特定sql过滤

1 多租户简介

什么叫多租户,什么场景下使用多租户。

多租户是一种软件架构技术,一套系统提供给多用户使用的环境,并且要注意数据之间的隔离性。

举个例子:假设我们有一个应用程序,这套程序有多个公司同时使用,在各个公司使用这个程序时会把用户相关数据传输到后台,在传输的时候需要带上用户标识(租户ID),以便后台将数据进行隔离。

简单来讲,就是共同一套系统,但各自的数据互不干扰,自己展现自己的数据。当不同的租户使用同一套程序,这里就需要考虑一个数据隔离的情况。

数据隔离有三种方案:

  1. 独立数据库

    这是第一种方案,即一个租户一个数据库,这种方案用户数据隔离级别最高,安全性最好,但成本较高。

    优点

    为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,恢复数据比较简单。

    缺点:

    增多了数据库的安装数量,随之带来维护成本和购置成本的增加。这种方案与传统的一个客户、一套数据、一套部署类似,差别只在于软件统一部署在运营商那里。如果面对的是银行、医院等需要非常高数据隔离级别的租户,可以选择这种模式。

  2. 共享数据库,隔离数据架构

    这是第二种方案,即多个或所有租户共享Database,但是每个租户一个Schema(也可叫做一个user)。

    优点:

    为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;每个数据库可支持更多的租户数量。

    缺点:

    如果出现故障,数据恢复比较困难,因为恢复数据库将牵涉到其他租户的数据;如果需要跨租户统计数据,存在一定困难。

  3. 共享数据库,共享数据架构

    这是第三种方案,即租户共享同一个Database、同一个Schema,但在表中增加TenantID多租户的数据字段。这是共享程度最高、隔离级别最低的模式。

    *优点: * 三种方案比较,第三种方案的维护和购置成本最低,允许每个数据库支持的租户数量最多。

    缺点:

    隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量;数据备份和恢复最困难,需要逐表逐条备份和还原。如果希望以最少的服务器为最多的租户提供服务,并且租户接受牺牲隔离级别换取降低成本,这种方案最适合。

2 多租户实现

多租户属于SQL解析部分,依赖分页插件,需要在分页插件中设置解析器。依赖分页插件不是只有分页方法实现多租户,它是因为拦截时期的问题,所以MP将其设计在这里使用。

这里我们使用第三种方案来实现。

在MybatisPlusConfiguration.java配置类中加入多租户插件,它是基于分页器实现的。

简单的实现流程就是在分页器paginationInterceptor中设置sql解析器接口,sql解析器接口中加入TenantSqlParser多租户解析器,业务代码无需任何修改,直接运行即可实现多租户管理。

/**
 * 多租户依赖于分页插件实现
 */
@Bean
public PaginationInterceptor paginationInterceptor() {
    PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
ArrayList<ISqlParser> sqlParserList = new ArrayList<>();    //  ISqlParser是sql解析器接口
TenantSqlParser tenantSqlParser = new TenantSqlParser();    //  TenantSqlParser是多租户解析器
tenantSqlParser.setTenantHandler(new TenantHandler() {
    @Override
    public Expression getTenantId(boolean where) {
        // 该 where 条件 3.2.0 版本开始添加的,用于区分是否为在 where 条件中使用
        // 如果是in/between之类的多个租户Id的情况,参考下方
        return new LongValue(1224581991756791809L);
    }

    @Override
    public String getTenantIdColumn() {
        return "manager_id";    //  设置表中对应的租户字段名
    }

    @Override
    public boolean doTableFilter(String tableName) {
        // 判断是否过滤表,即这些表不加租户信息 false为加,true为不加
        /*if ("user".equals(tableName)) {
            return true;
        }*/
        return false;
    }
});

sqlParserList.add(tenantSqlParser);
paginationInterceptor.setSqlParserList(sqlParserList);  //  配置到分页器中

return paginationInterceptor;

}

代码中租户处理器TenantHandler中有三个重写方法:

getTenantId(boolean where):获取租户id,可以从cookie读取,这里写成固定值;

getTenantIdColumn():设置表中对应的租户字段名,我这里的租户id用的是manager_id字段;

doTableFilter(String tableName):判断是否过滤表,即这些表不加租户信息约束。

image
image

我这里随便测试一个方法selectUser,可以在控制台打印看到sql语句自动加上了租户id条件,原理就是通过sql解析器ISqlParser改写SQL语句,加入了我们设置的getTenantIdColumn字段对应的getTenantId值。doTableFilter方法可以放开代码中注释进行测试。

CRUD的操作都会加上租户信息,其中插入方法我们不用设置租户id,mp会自动帮我们在insert语句中加入我们配置好的租户id。下图是插入操作演示:

image
image

2 特定sql过滤

我们在使用多租户的时候,有些接口不需要启用多租户配置。

一种方法可以在MybatisPlusConfiguration类中分页插件paginationInterceptor设置一个SqlParser过滤器,用来过滤指定sql,使其不执行多租户约束,代码如下:

//  过滤指定sql,使其不执行多租户约束
paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
    @Override
    public boolean doFilter(MetaObject metaObject) {
        MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);
        // 过滤自定义查询此时无租户信息约束
        if ("com.ethan.dao.UserMapper.mySelectUsers".equals(ms.getId())) {
            return true;
        }
        return false;
    }
});

另一种方法可以使用注解的形式,这也是推荐的一种启用多租户配置的方式。在方法上加上注解@SqlParser(filter = true),使用注解的方式极大简化了我们代码量。

@SqlParser(filter = true)   //  使用注解配置接口是否启动多租户,true为不启用,默认为false启用
@Select("select * from user ${ew.customSqlSegment}")
List<User> mySelectUsers(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
兜里的猫 小程序

dvdf

欢迎访问博客网页版:www.wldeer.com

评论

稻城 : 111
兜里的猫 : 是md
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×