mybatis-plus实现多租户Saas

mybatis-plus实现多租户Saas

实现方式

给对应需要进行数据隔离的表加上租户ID (tenant_id字段)

原理

使用mybatis-plus操作的表会自动过滤tenant_id字段的值
包括新增,修改,删除,查询,关联查询等

步骤

1.给对应表加上tenant_id字段

2.配置mybatis-plus

项目结构

mybatis-plus实现多租户Saas_第1张图片

初始数据

DROP TABLE IF EXISTS user;

CREATE TABLE user
(
	id BIGINT(20) NOT NULL COMMENT '主键ID',
	tenant_id BIGINT(20) NOT NULL COMMENT '租户ID',
	name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
	PRIMARY KEY (id)
);

DROP TABLE IF EXISTS user_addr;

CREATE TABLE USER_ADDR
(
  id BIGINT(20) NOT NULL COMMENT '主键ID',
  user_id BIGINT(20) NOT NULL COMMENT 'user.id',
  name VARCHAR(30) NULL DEFAULT NULL COMMENT '地址名称',
  PRIMARY KEY (id)
);

DELETE FROM user;

INSERT INTO user (id, tenant_id, name) VALUES
(1, 1, 'Jone'),(2, 1, 'Jack'),(3, 1, 'Tom'),
(4, 0, 'Sandy'),(5, 0, 'Billie');

INSERT INTO user_addr (id, USER_ID, name) VALUES
(1, 1, 'addr1'),(2,1,'addr2');

Maven依赖



    
        mybatis-plus-samples
        com.baomidou
        0.0.1-SNAPSHOT
    
    4.0.0

    mybatis-plus-sample-tenant

    
        
            org.springframework.boot
            spring-boot-starter
        
        
            com.baomidou
            mybatis-plus-boot-starter
        
        
        
            mysql
            mysql-connector-java
        
    

    
        
            
                src/main/java
                false
                
                    **/mapper/*.xml
                
            
            
                src/main/resources
                
                    **/*
                
            
        
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

yml配置

# DataSource Config
spring:
  datasource:
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/rest?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true



# Logger Config
logging:
  level:
    com.baomidou.mybatisplus.samples: debug
# MyBatis-Plus 配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志打印
system:
  saas:
    ignoreTables: # 需要忽略的表,表中无租户ID字段
      - user_addr

代码

MybatisPlusConfig.java

package com.baomidou.mybatisplus.samples.tenant.config;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;

import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;

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


@Configuration
@MapperScan("com.baomidou.mybatisplus.samples.tenant.mapper")
public class MybatisPlusConfig {

    @Autowired
    private SaaSConfig saaSConfig;
    /**
     * 新多租户插件配置,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存万一出现问题
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加租户
        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
            // todo 获取当前用户租户ID
            Long tenantId = 1L;

            /**
             * 设置租户ID的值
             * @return
             */
            @Override
            public Expression getTenantId() {
                return new LongValue(tenantId);
            }

            // 这是 default 方法,默认返回 tenant_id 表示租户ID字段名称,租户字段为tenant_id时,不用重写此方法
            @Override
            public String getTenantIdColumn() {
                return "tenant_id";
            }

            // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
            @Override
            public boolean ignoreTable(String tableName) {
                // 超级管理员端的表可以进行忽略(如 租户管理表等)
                List ignoreTables = saaSConfig.getIgnoreTables();
                long count = ignoreTables.stream().filter(e -> e.equalsIgnoreCase(tableName)).count();
                return count > 0;
                //return !"user".equalsIgnoreCase(tableName);
            }
        }));
        // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
        // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false
//        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }

//    @Bean
//    public ConfigurationCustomizer configurationCustomizer() {
//        return configuration -> configuration.setUseDeprecatedExecutor(false);
//    }
}

SaaSConfig.java

package com.baomidou.mybatisplus.samples.tenant.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

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

/**
 * 多租户的配置属性
 *
 * @company
 *
 */
@Data
@Component
@ConfigurationProperties(prefix = "system.saas")
public class SaaSConfig {

	/**
	 * 多租户字段名
	 */
	private String tenantId = "tenant_id";
	
	/**
	 * 忽略多租户的表名
	 * 
	 * 数据库中物理表表名
	 * 
*/ private List ignoreTables = new ArrayList<>(); }

User.java

package com.baomidou.mybatisplus.samples.tenant.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;

import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.experimental.Accessors;

/**
 * 

* 用户实体对应表 user *

* */ @Data @Accessors(chain = true) public class User { @TableId(type = IdType.AUTO) private Long id; /** * 租户 ID */ private Long tenantId; private String name; @TableField(exist = false) private String addrName; }

UserMapper.java

package com.baomidou.mybatisplus.samples.tenant.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.samples.tenant.entity.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * 

* MP 支持不需要 UserMapper.xml 这个模块演示内置 CRUD 咱们就不要 XML 部分了 *

* */ public interface UserMapper extends BaseMapper { /** * 自定义SQL:默认也会增加多租户条件 * 参考打印的SQL * @return */ Integer myCount(); List getUserAndAddr(@Param("username") String username); List getAddrAndUser(@Param("name") String name); }

UserMapper.xml





    

    

    

TenantTest.java

package com.baomidou.mybatisplus.samples.tenant;

import com.baomidou.mybatisplus.samples.tenant.entity.User;
import com.baomidou.mybatisplus.samples.tenant.mapper.UserMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.List;

/**
 * 

* 多租户 Tenant 演示 *

* * @author hubin * @since 2018-08-11 */ @SpringBootTest public class TenantTest { @Resource private UserMapper mapper; @Test public void aInsert() { User user = new User(); user.setName("一一33"); Assertions.assertTrue(mapper.insert(user) > 0); user = mapper.selectById(user.getId()); Assertions.assertTrue(1 == user.getTenantId()); } @Test public void bDelete() { Assertions.assertTrue(mapper.deleteById(3L) > 0); } @Test public void cUpdate() { Assertions.assertTrue(mapper.updateById(new User().setId(1L).setName("mp")) > 0); } @Test public void dSelect() { List userList = mapper.selectList(null); userList.forEach(u -> Assertions.assertTrue(1 == u.getTenantId())); } /** * 自定义SQL:默认也会增加多租户条件 * 参考打印的SQL */ @Test public void manualSqlTenantFilterTest() { System.out.println(mapper.myCount()); } @Test public void testTenantFilter(){ mapper.getAddrAndUser(null).forEach(System.out::println); mapper.getAddrAndUser("add").forEach(System.out::println); mapper.getUserAndAddr(null).forEach(System.out::println); mapper.getUserAndAddr("J").forEach(System.out::println); } }

参考:https://baomidou.com/pages/aef2f2/#tenantlineinnerinterceptor

你可能感兴趣的:(常用,mybatis,java,spring,boot)