九天猎人 发表于 2024-12-12 02:01:28

基于 Mybatis-plus 实现多租户架构

什么是多租户技术


多租户技术(英语:multi-tenancy technology)或称多重租赁技术,是一种软件架构技术,它是在探讨与实现如何于多用户的情况下共用类似的系统或步伐组件,并且仍可确保各用户间数据的隔离性。在云计算的加持之下,多租户技术被广为运用于开发各式云服务,不论是IaaS,PaaS照旧SaaS,都可以看到多租户技术的影子。多租户技术就是让一套系统通过配置给不同的客户提供服务,每个客户看到的数据都是属于本身的,就似乎每个客户都拥有本身一套独立美满的系统。
多租户实现的方式


数据库隔离:在一个数据库实例中创建多个租户的数据库,每个租户只能访问本身的数据库。这种方式实现简朴,但是倒霉于扩展和维护。
数据表隔离:在一个数据库中创建一张租户表,全部租户的数据存储在同一张表中,但是可以根据租户ID举行数据隔离。这种方式比较机动,但是需要在应用步伐中手动处置惩罚租户ID。
模式隔离:在一个数据库中创建多个模式(Schema),每个租户被分配到独立的模式中。这种方式比较机动,支持多个租户共用同一张表,但是需要在应用步伐中手动处置惩罚模式切换。
实例隔离:在一个应用步伐中创建多个实例,每个租户被分配到独立的实例中。这种方式维护成本较高,但是支持多个租户之间的完全隔离。
肴杂模式:可以选择以上几种方式的组合实现多租户。例如,数据表隔离和模式隔离的结合方式,可以在一个数据库中创建多个租户表和多个租户模式,以增长机动性和数据隔离性。
Mybatis-plus的实现方式


01界说表结构


CREATE TABLE `user` (`id` bigint(20) NOT NULL,`name` varchar(20) DEFAULT NULL,`phone` varchar(11) DEFAULT NULL,`address` varchar(64) DEFAULT NULL,`tenant_id` bigint(20) DEFAULT NULL,PRIMARY KEY (`id`))CREATE TABLE `dept_zhang` (`id` bigint(20) NOT NULL,`dept_name` varchar(64) DEFAULT NULL,`comment` varchar(128) DEFAULT NULL,PRIMARY KEY (`id`)) 第一个表底子字段后都添加租户字段tenant_id,第二个表名加租户信息
02导入依赖


<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.2</version></dependency><dependency> 03配置mybatis-plus多租户功能


配置DynamicTableNameInnerInterceptor
主要是用来对某些数据量大的表做分表查询的,这个拦截器可以在执行sql语句的时候动态的修改查询的表名。
//这里我将租户id写死了。真实的实现中应当从当前登录的数据中获取  
private static final String tenant_id = "zhang";
private DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor= new DynamicTableNameInnerInterceptor();
TableNameHandler tableNameHandler = new TableNameHandler() {
      @Override
      public String dynamicTableName(String sql, String tableName) {
          //tenantProperty中动态标识的是哪些表是分表的表,就在哪些分表的表添加租户的表后缀
          //可以将tenantProperty的配置修改为数据库配置也可以,改动更灵活
          if (tenantProperty.getShardingTables().contains(tableName)){
            return tableName+"_"+tenant_id;
          }
          return tableName;
      }
};
dynamicTableNameInnerInterceptor.setTableNameHandler(tableNameHandler);
return dynamicTableNameInnerInterceptor;
} 配置TenantLineInnerInterceptor
Mybatis自带的无自界说的租户的拦截器会在全部的sql后面加上对应的租户条件,但是我们可以自界说对应的处置惩罚租户信息相关的Handler.
public class MultiTenantLineHandler implements TenantLineHandler {

    private TenantProperty tenantProperty;

    public MultiTenantLineHandler(TenantProperty tenantProperty){
      this.tenantProperty =tenantProperty;
    }
    @Override
    public Expression getTenantId() {
      //此处直接使用给定租户,实际实现从登录信息中取出
            return new StringValue("zhang");
    }

    @Override
    public String getTenantIdColumn() {
      //租户列名
      return tenantProperty.getTenantColumn();
    }
    //需要忽略的表的配置
    @Override
    public boolean ignoreTable(String tableName) {
      List<String> ignoreTables = tenantProperty.getIgnoreTables();
      if (ignoreTables.contains(tableName)){
            return true;
      }
      return false;
    }
//    不处理的非租户列的insert
//    @Override
//    public boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {
//      return TenantLineHandler.super.ignoreInsert(columns, tenantIdColumn);
//    }
} 04自界说配置类

配置类​​​​​​​
@Data@Configuration@ConfigurationProperties(prefix = "tenant")public class TenantProperty {    private Boolean enable =true;    private String tenantColumn="tenant_id";    private List<String> ignoreTables;    private List<String> shardingTables;} 配置信息​​​​​​​
tenant:enable: trueignoreTables:    - deptshardingTables:    - dept
05整合拦截器​​​​​​​
@Configuration
@MapperScan("com.zhang.test.dao")
public class TableTenantConfig {
    private static final String tenant_id = "zhang";
    @Autowired    TenantProperty tenantProperty;
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
      MybatisPlusInterceptor interceptor =new MybatisPlusInterceptor();
      interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new MultiTenantLineHandler(tenantProperty)));
      interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());
      //注入动态表名适配拦截器解决多表名问题
      interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
      interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
      return interceptor;
    }
} 06查询效果
@RestController@RequestMapping("tenant")public class TenantController {
    @Autowired    ShardingMapper deptMapper;    @Autowired    TenantMapper tenantMapper;
    @GetMapping("tenant")    public Tenant user(){      return tenantMapper.selectById(1L);    }
    @GetMapping("dept")    public Dept dept(){      return deptMapper.selectById(1L);    }} 说明:

[*] 查询user表时,SQL是主动拼接tenant_id='zhang'条件
[*] 查询dept表时,数据库表名会主动改为dept_zhang
https://i-blog.csdnimg.cn/direct/d47832190c6b43058ea281c26eb9a884.jpeghttps://i-blog.csdnimg.cn/direct/e819c5aa5912421e9a323ed1474c9ded.jpeg



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 基于 Mybatis-plus 实现多租户架构