伙伴云客服论坛»论坛 S区 S产品资讯 查看内容

0 评论

0 收藏

分享

mybatis-plus实现多租户Saas

mybatis-plus实现多租户Saas

实现方式

给对应需要停止数据隔离的表加上租户ID (tenant_id字段)
原理

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

1.给对应表加上tenant_id字段

2.配置mybatis-plus

项目构造

mybatis-plus实现多租户Saas-1.png


初始数据
  1. DROP TABLE IF EXISTS user;
  2. CREATE TABLE user
  3. (
  4.         id BIGINT(20) NOT NULL COMMENT '主键ID',
  5.         tenant_id BIGINT(20) NOT NULL COMMENT '租户ID',
  6.         name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
  7.         PRIMARY KEY (id)
  8. );
  9. DROP TABLE IF EXISTS user_addr;
  10. CREATE TABLE USER_ADDR
  11. (
  12.   id BIGINT(20) NOT NULL COMMENT '主键ID',
  13.   user_id BIGINT(20) NOT NULL COMMENT 'user.id',
  14.   name VARCHAR(30) NULL DEFAULT NULL COMMENT '地址名称',
  15.   PRIMARY KEY (id)
  16. );
  17. DELETE FROM user;
  18. INSERT INTO user (id, tenant_id, name) VALUES
  19. (1, 1, 'Jone'),(2, 1, 'Jack'),(3, 1, 'Tom'),
  20. (4, 0, 'Sandy'),(5, 0, 'Billie');
  21. INSERT INTO user_addr (id, USER_ID, name) VALUES
  22. (1, 1, 'addr1'),(2,1,'addr2');
复制代码
Maven依赖
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4.     <parent>
  5.         <artifactId>mybatis-plus-samples</artifactId>
  6.         <groupId>com.baomidou</groupId>
  7.         <version>0.0.1-SNAPSHOT</version>
  8.     </parent>
  9.     <modelVersion>4.0.0</modelVersion>
  10.     <artifactId>mybatis-plus-sample-tenant</artifactId>
  11.     <dependencies>
  12.         <dependency>
  13.             <groupId>org.springframework.boot</groupId>
  14.             <artifactId>spring-boot-starter</artifactId>
  15.         </dependency>
  16.         <dependency>
  17.             <groupId>com.baomidou</groupId>
  18.             <artifactId>mybatis-plus-boot-starter</artifactId>
  19.         </dependency>
  20.         <!-- MySQL 连接驱动依赖 -->
  21.         <dependency>
  22.             <groupId>mysql</groupId>
  23.             <artifactId>mysql-connector-java</artifactId>
  24.         </dependency>
  25.     </dependencies>
  26.     <build>
  27.         <resources>
  28.             <resource>
  29.                 <directory>src/main/java</directory>
  30.                 <filtering>false</filtering>
  31.                 <includes>
  32.                     <include>**/mapper/*.xml</include>
  33.                 </includes>
  34.             </resource>
  35.             <resource>
  36.                 <directory>src/main/resources</directory>
  37.                 <includes>
  38.                     <include>**/*</include>
  39.                 </includes>
  40.             </resource>
  41.         </resources>
  42.         <plugins>
  43.             <plugin>
  44.                 <groupId>org.springframework.boot</groupId>
  45.                 <artifactId>spring-boot-maven-plugin</artifactId>
  46.             </plugin>
  47.         </plugins>
  48.     </build>
  49. </project>
复制代码
yml配置
  1. # DataSource Config
  2. spring:
  3.   datasource:
  4.     username: root
  5.     password: root
  6.     driver-class-name: com.mysql.cj.jdbc.Driver
  7.     url: jdbc:mysql://localhost:3306/rest?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true
  8. # Logger Config
  9. logging:
  10.   level:
  11.     com.baomidou.mybatisplus.samples: debug
  12. # MyBatis-Plus 配置
  13. mybatis-plus:
  14.   configuration:
  15.     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志打印
  16. system:
  17.   saas:
  18.     ignoreTables: # 需要忽略的表,表中无租户ID字段
  19.       - user_addr
复制代码
代码

MybatisPlusConfig.java
  1. package com.baomidou.mybatisplus.samples.tenant.config;
  2. import org.mybatis.spring.annotation.MapperScan;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
  7. import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
  8. import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
  9. import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
  10. import net.sf.jsqlparser.expression.Expression;
  11. import net.sf.jsqlparser.expression.LongValue;
  12. import java.util.ArrayList;
  13. import java.util.Arrays;
  14. import java.util.List;
  15. @Configuration
  16. @MapperScan("com.baomidou.mybatisplus.samples.tenant.mapper")
  17. public class MybatisPlusConfig {
  18.     @Autowired
  19.     private SaaSConfig saaSConfig;
  20.     /**
  21.      * 新多租户插件配置,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 防止缓存万一呈现问题
  22.      */
  23.     @Bean
  24.     public MybatisPlusInterceptor mybatisPlusInterceptor() {
  25.         MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  26.         // 添加租户
  27.         interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
  28.             // todo 获取当前用户租户ID
  29.             Long tenantId = 1L;
  30.             /**
  31.              * 设置租户ID的值
  32.              * @return
  33.              */
  34.             @Override
  35.             public Expression getTenantId() {
  36.                 return new LongValue(tenantId);
  37.             }
  38.             // 这是 default 方法,默认返回 tenant_id 表示租户ID字段名称,租户字段为tenant_id时,不用重写此方法
  39.             @Override
  40.             public String getTenantIdColumn() {
  41.                 return "tenant_id";
  42.             }
  43.             // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
  44.             @Override
  45.             public boolean ignoreTable(String tableName) {
  46.                 // 超级管理员端的表可以停止忽略(如 租户管理表等)
  47.                 List<String> ignoreTables = saaSConfig.getIgnoreTables();
  48.                 long count = ignoreTables.stream().filter(e -> e.equalsIgnoreCase(tableName)).count();
  49.                 return count > 0;
  50.                 //return !"user".equalsIgnoreCase(tableName);
  51.             }
  52.         }));
  53.         // 假设用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
  54.         // 用了分页插件必需设置 MybatisConfiguration#useDeprecatedExecutor = false
  55. //        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
  56.         return interceptor;
  57.     }
  58. //    @Bean
  59. //    public ConfigurationCustomizer configurationCustomizer() {
  60. //        return configuration -> configuration.setUseDeprecatedExecutor(false);
  61. //    }
  62. }
复制代码
SaaSConfig.java
  1. package com.baomidou.mybatisplus.samples.tenant.config;
  2. import lombok.Data;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.stereotype.Component;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. /**
  8. * 多租户的配置属性
  9. *
  10. * @company
  11. *
  12. */
  13. @Data
  14. @Component
  15. @ConfigurationProperties(prefix = "system.saas")
  16. public class SaaSConfig {
  17.         /**
  18.          * 多租户字段名
  19.          */
  20.         private String tenantId = "tenant_id";
  21.        
  22.         /**
  23.          * 忽略多租户的表名
  24.          * <pre>
  25.          * 数据库中物理表表名
  26.          * </pre>
  27.          */
  28.         private List<String> ignoreTables = new ArrayList<>();
  29.        
  30. }
复制代码
User.java
  1. package com.baomidou.mybatisplus.samples.tenant.entity;
  2. import com.baomidou.mybatisplus.annotation.IdType;
  3. import com.baomidou.mybatisplus.annotation.TableField;
  4. import com.baomidou.mybatisplus.annotation.TableId;
  5. import lombok.Data;
  6. import lombok.experimental.Accessors;
  7. /**
  8. * <p>
  9. * 用户实体对应表 user
  10. * </p>
  11. *
  12. */
  13. @Data
  14. @Accessors(chain = true)
  15. public class User {
  16.     @TableId(type = IdType.AUTO)
  17.     private Long id;
  18.     /**
  19.      * 租户 ID
  20.      */
  21.     private Long tenantId;
  22.     private String name;
  23.     @TableField(exist = false)
  24.     private String addrName;
  25. }
复制代码
UserMapper.java
  1. package com.baomidou.mybatisplus.samples.tenant.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.baomidou.mybatisplus.samples.tenant.entity.User;
  4. import org.apache.ibatis.annotations.Param;
  5. import java.util.List;
  6. /**
  7. * <p>
  8. * MP 支持不需要 UserMapper.xml 这个模块演示内置 CRUD 咱们就不要 XML 部分了
  9. * </p>
  10. *
  11. */
  12. public interface UserMapper extends BaseMapper<User> {
  13.     /**
  14.      * 自定义SQL:默认也会增加多租户条件
  15.      * 参考打印的SQL
  16.      * @return
  17.      */
  18.     Integer myCount();
  19.     List<User> getUserAndAddr(@Param("username") String username);
  20.     List<User> getAddrAndUser(@Param("name") String name);
  21. }
复制代码
UserMapper.xml
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
  3. <mapper namespace="com.baomidou.mybatisplus.samples.tenant.mapper.UserMapper">
  4.     <select id="myCount" resultType="java.lang.Integer">
  5.         select count(1) from user
  6.     </select>
  7.     <select id="getUserAndAddr" resultType="com.baomidou.mybatisplus.samples.tenant.entity.User">
  8.         select u.id, u.name, a.name as addr_name
  9.         from user u
  10.         left join user_addr a on a.user_id=u.id
  11.         <where>
  12.             <if test="username!=null">
  13.                 u.name like concat(concat('%',#{username}),'%')
  14.             </if>
  15.         </where>
  16.     </select>
  17.     <select id="getAddrAndUser" resultType="com.baomidou.mybatisplus.samples.tenant.entity.User">
  18.         select a.name as addr_name, u.id, u.name
  19.         from user_addr a
  20.         left join user u on u.id=a.user_id
  21.         <where>
  22.             <if test="name!=null">
  23.                 a.name like concat(concat('%',#{name}),'%')
  24.             </if>
  25.         </where>
  26.     </select>
  27. </mapper>
复制代码
TenantTest.java
  1. package com.baomidou.mybatisplus.samples.tenant;
  2. import com.baomidou.mybatisplus.samples.tenant.entity.User;
  3. import com.baomidou.mybatisplus.samples.tenant.mapper.UserMapper;
  4. import org.junit.jupiter.api.Assertions;
  5. import org.junit.jupiter.api.Test;
  6. import org.springframework.boot.test.context.SpringBootTest;
  7. import javax.annotation.Resource;
  8. import java.util.List;
  9. /**
  10. * <p>
  11. * 多租户 Tenant 演示
  12. * </p>
  13. *
  14. * @author hubin
  15. * @since 2018-08-11
  16. */
  17. @SpringBootTest
  18. public class TenantTest {
  19.     @Resource
  20.     private UserMapper mapper;
  21.     @Test
  22.     public void aInsert() {
  23.         User user = new User();
  24.         user.setName("逐个33");
  25.         Assertions.assertTrue(mapper.insert(user) > 0);
  26.         user = mapper.selectById(user.getId());
  27.         Assertions.assertTrue(1 == user.getTenantId());
  28.     }
  29.     @Test
  30.     public void bDelete() {
  31.         Assertions.assertTrue(mapper.deleteById(3L) > 0);
  32.     }
  33.     @Test
  34.     public void cUpdate() {
  35.         Assertions.assertTrue(mapper.updateById(new User().setId(1L).setName("mp")) > 0);
  36.     }
  37.     @Test
  38.     public void dSelect() {
  39.         List<User> userList = mapper.selectList(null);
  40.         userList.forEach(u -> Assertions.assertTrue(1 == u.getTenantId()));
  41.     }
  42.     /**
  43.      * 自定义SQL:默认也会增加多租户条件
  44.      * 参考打印的SQL
  45.      */
  46.     @Test
  47.     public void manualSqlTenantFilterTest() {
  48.         System.out.println(mapper.myCount());
  49.     }
  50.     @Test
  51.     public void testTenantFilter(){
  52.         mapper.getAddrAndUser(null).forEach(System.out::println);
  53.         mapper.getAddrAndUser("add").forEach(System.out::println);
  54.         mapper.getUserAndAddr(null).forEach(System.out::println);
  55.         mapper.getUserAndAddr("J").forEach(System.out::println);
  56.     }
  57. }
复制代码
参考:https://baomidou.com/pages/aef2f2/#tenantlineinnerinterceptor

回复

举报 使用道具

全部回复
暂无回帖,快来参与回复吧
本版积分规则 高级模式
B Color Image Link Quote Code Smilies

开心姐妹群
注册会员
主题 16
回复 26
粉丝 0
|网站地图
快速回复 返回顶部 返回列表