我无论作什麽,始终在想着,只要我的精力允许我的话,我就要首先为我的祖国服务。——巴甫

个人理解的多租户:一套产品提供给多个企业使用,每家企业之间的数据相互隔离。例如我有一套运输管理系统,开发完成后,每一家企业购买我们的产品,我只需要提供一个账号,即可拥有完整的内容。如权限管理、订单管理等,他们之间的数据是不互通的

我们可以通过在每张表上加一个租户id去实现这个功能

我们的mybatis-plus版本为3.1.0,这里放上依赖

1
2
3
4
5
6
<!--    mybatis-plus    -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>

然后我们需要修改配置文件

1
2
3
4
5
6
7
8
9
10
11
#mybatis的配置
mybatis-plus:
config-location: classpath:/mybatis/mybatis-config.xml
mapper-locations:
- classpath*:com/ruben/dao/xml/*Mapper.xml
configuration-properties:
prefix:
boolValue: TRUE
blobType: BLOB
global-config:
sql-parser-cache: true

关键是最下面两行sql-parser-cache: true

然后我们需要在Mybatis-Plus的配置类中进行配置

我们之前注入的分页拦截器PaginationInterceptor

当时是这么写的

1
2
3
4
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}

现在我们加上配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.ruben.config;

import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
* @ClassName: MybatisPlusConfig
* @Description: MybatisPlus配置
* @Date: 2020/11/21 0021 16:34
* *
* @author: <achao1441470436@gmail.com>
* @version: 1.0
* @since: JDK 1.8
*/
@Configuration
@MapperScan("com.ruben.dao.mapper*")
public class MybatisPlusConfig {

@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor interceptor = new PaginationInterceptor();
List<ISqlParser> sqlParserList = new ArrayList<>();
sqlParserList.add(new BlockAttackSqlParser());
TenantSqlParser tenantSqlParser = new TenantSqlParser();
tenantSqlParser.setTenantHandler(new TenantHandler() {
@Override
public Expression getTenantId() {
// 配置租户id,这里我简单使用1作为租户id
return new LongValue(1);
}

@Override
public String getTenantIdColumn() {
// 配置租户id字段
return "tenant_id";
}

@Override
public boolean doTableFilter(String tableName) {
// 配置是否过滤一些表,例如如果需要过滤 user 表,则:if (tableName.equals('user')) return true;
return false;
}
});
sqlParserList.add(tenantSqlParser);
interceptor.setSqlParserList(sqlParserList);
return interceptor;
}
}

别忘了在数据库中的表加上字段tenant_id,如果我们的表非常多,可以参考我这篇博客统一加字段

然后如果我们有些sql或者函数不需要加租户条件,则可以使用@SqlParser(filter = true)注解

image-20210213182615411

不过注意,只能在extends com.baomidou.mybatisplus.core.mapper.BaseMapper的类中函数上加该注解才能生效

我们简单测试一下

1
2
3
4
5
6
7
8
@Test
public void list() {
List<UserPO> userList = mpUserMapper.findUserList(UserPO.builder().username("ruben").build());
userList.forEach(System.out::println);
LambdaQueryWrapper<UserPO> wrapper = Wrappers.lambdaQuery(UserPO.builder().build());
wrapper.select(w -> true).inSql(UserPO::getId, "select id from user");
mpUserMapper.selectList(wrapper);
}

image-20210213182928977

这里红色打印出来的sql就是我们刚刚加了@SqlParser(filter = true)注解的函数

下面蓝色则是使用mybatis-plus封装好的selectList方法,可以看出蓝色里我们配置了租户id后的结果,默认加上了 user.tenant_id = 1条件

这样每个租户之间就实现了相互隔离,非常便利~