Mybatis-Plus 3.4.0多租户的实现方案
Posted MateCloud微服务
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis-Plus 3.4.0多租户的实现方案相关的知识,希望对你有一定的参考价值。
使用背景
微服务matecloud希望支持saas的多租户的管理模式,mybatis plus正好已经考虑支持该模式,下面就简单说说其应用案例
多租户模式介绍
数据隔离有三种方案:
1、独立数据库:简单来说就是一个租户使用一个数据库,这种数据隔离级别最高,安全性最好,但是提高成本。
2、共享数据库、隔离数据架构:多租户使用同一个数据裤,但是每个租户对应一个Schema(数据库user)。-
3、共享数据库、共享数据架构:使用同一个数据库,同一个Schema,但是在表中增加了租户ID的字段,这种共享数据程度最高,隔离级别最低。下面样例以共郭数据库、共享数据结构的形式的讲述
集成步骤
1. 加依赖
<!--Mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.4.0</version>
</dependency>
主要是这两个依赖
2. 写代码
2.1. 租户属性
package vip.mate.core.database.props;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 租户属性
* @author xuzhanfu
* @Date 2020-9-6
*/
@Getter
@Setter
@RefreshScope
@ConfigurationProperties(prefix = "mate.tenant")
public class TenantProperties
/**
* 是否开启租户模式
*/
private Boolean enable = true;
/**
* 需要排除的多租户的表
*/
private List<String> ignoreTables = Arrays.asList("mate_sys_user", "mate_sys_depart", "mate_sys_role", "mate_sys_tenant", "mate_sys_role_permission");
/**
* 多租户字段名称
*/
private String column = "tenant_id";
/**
* 排除不进行租户隔离的sql
* 样例全路径:vip.mate.system.mapper.UserMapper.findList
*/
private List<String> ignoreSqls = new ArrayList<>();
2.2. 租户配置
package vip.mate.core.database.config;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import lombok.AllArgsConstructor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.StringValue;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import vip.mate.core.common.context.TenantContextHolder;
import vip.mate.core.database.props.TenantProperties;
/**
* 多租户配置中心
* @author pangu
* @Date 2020-9-7
*/
@Configuration
@AllArgsConstructor
@AutoConfigureBefore(MybatisPlusConfiguration.class)
@EnableConfigurationProperties(TenantProperties.class)
public class TenantConfiguration
private final TenantProperties tenantProperties;
/**
* 新多租户插件配置,一缓和二缓遵循mybatis的规则,
* 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
* 避免缓存万一出现问题
* @return
*/
@Bean
public TenantLineInnerInterceptor tenantLineInnerInterceptor()
return new TenantLineInnerInterceptor(new TenantLineHandler()
/**
* 获取租户ID
* @return
*/
@Override
public Expression getTenantId()
String tenant = TenantContextHolder.getTenantId();
if (tenant != null)
return new StringValue(TenantContextHolder.getTenantId());
return new NullValue();
/**
* 获取多租户的字段名
* @return String
*/
@Override
public String getTenantIdColumn()
return tenantProperties.getColumn();
/**
* 过滤不需要根据租户隔离的表
* 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
* @param tableName 表名
*/
@Override
public boolean ignoreTable(String tableName)
return tenantProperties.getIgnoreTables().stream().anyMatch(
(t) -> t.equalsIgnoreCase(tableName)
);
);
2.3. Mybatis plus配置
package vip.mate.core.database.config;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import vip.mate.core.common.factory.YamlPropertySourceFactory;
import vip.mate.core.database.handler.MateMetaObjectHandler;
import vip.mate.core.database.props.TenantProperties;
/**
* mybatis plus配置中心
* @author xuzhanfu
*/
@Slf4j
@Configuration
@AllArgsConstructor
@EnableTransactionManagement
@PropertySource(factory = YamlPropertySourceFactory.class, value = "classpath:mate-db.yml")
@MapperScan("vip.mate.**.mapper.**")
public class MybatisPlusConfiguration
private final TenantProperties tenantProperties;
private final TenantLineInnerInterceptor tenantLineInnerInterceptor;
/**
* 单页分页条数限制(默认无限制,参见 插件#handlerLimit 方法)
*/
private static final Long MAX_LIMIT = 1000L;
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,
* 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
* 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor paginationInterceptor()
boolean enableTenant = tenantProperties.getEnable();
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
if (enableTenant)
interceptor.addInnerInterceptor(tenantLineInnerInterceptor);
//分页插件: PaginationInnerInterceptor
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setMaxLimit(MAX_LIMIT);
//防止全表更新与删除插件: BlockAttackInnerInterceptor
BlockAttackInnerInterceptor blockAttackInnerInterceptor = new BlockAttackInnerInterceptor();
interceptor.addInnerInterceptor(paginationInnerInterceptor);
interceptor.addInnerInterceptor(blockAttackInnerInterceptor);
return interceptor;
@Bean
public ConfigurationCustomizer configurationCustomizer()
return configuration -> configuration.setUseDeprecatedExecutor(Boolean.FALSE);
/**
* 自动填充数据
*/
@Bean
@ConditionalOnMissingBean(MateMetaObjectHandler.class)
public MateMetaObjectHandler mateMetaObjectHandler()
MateMetaObjectHandler metaObjectHandler = new MateMetaObjectHandler();
log.info("MateMetaObjectHandler []", metaObjectHandler);
return metaObjectHandler;
注意看下注释
剩下的就是前端将tenant_id这个字段全局设定,并传过来即可
代码样例
https://github.com/matevip/matecloud
以上是关于Mybatis-Plus 3.4.0多租户的实现方案的主要内容,如果未能解决你的问题,请参考以下文章
如何解决使用mybatis-plus提供的多租户插件出现Column ‘tenant_id‘ specified twice问题
JeecgBoot开发多租户SAAS数据隔离,查询数据库,改造多租户后 Mybatis-plus 查询数据库的SQL语句tenant-id[租户Id] 一直为0
JeecgBoot开发多租户SAAS数据隔离,查询数据库,改造多租户后 Mybatis-plus 查询数据库的SQL语句tenant-id[租户Id] 一直为0
Idea+maven+spring-cloud项目搭建系列--13 整合MyBatis-Plus多数据源dynamic-datasource