当前位置:   article > 正文

mybatis plus实现多租户方案_baomidou mapper.xml 多租户

baomidou mapper.xml 多租户

注意:方案的数据库mapper层采用的是tk.mybatis

maven依赖

  1. <dependency>
  2. <groupId>com.baomidou</groupId>
  3. <artifactId>mybatis-plus-boot-starter</artifactId>
  4. <version>3.3.2</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.github.jsqlparser</groupId>
  8. <artifactId>jsqlparser</artifactId>
  9. <version>4.5</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>tk.mybatis</groupId>
  13. <artifactId>mapper-spring-boot-starter</artifactId>
  14. <version>2.1.5</version>
  15. <exclusions>
  16. <exclusion>
  17. <artifactId>mybatis</artifactId>
  18. <groupId>org.mybatis</groupId>
  19. </exclusion>
  20. </exclusions>
  21. </dependency>

多租户插件类 MultiTenantPlugin

  1. public class MultiTenantPlugin implements Interceptor{
  2. private static PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
  3. private static Set<String> multiTenantTable = new HashSet<>();
  4. @Value("${multi.tenant.table:}")
  5. private String multiTenantTables;
  6. @Value("${multi.tenant.enable:}")
  7. private boolean multiTenantEnable;
  8. @PostConstruct
  9. public void afterPropertiesSet() {
  10. if (multiTenantEnable) {
  11. initMultiTenantTable();
  12. initTenantSqlParser();
  13. }
  14. }
  15. /**
  16. * 初始化多租户Dao层sql相关转换器
  17. */
  18. private void initTenantSqlParser() {
  19. List<ISqlParser> sqlParserList = new ArrayList<>();
  20. TenantSqlParser tenantSqlParser = new TenantSqlParser();
  21. tenantSqlParser.setTenantHandler(new TenantHandler() {
  22. @Override
  23. public Expression getTenantId(boolean select) {
  24. if (multiTenantEnable) {
  25. return new StringValue(RequestHolder.getTenantId());
  26. } else {
  27. String tenantId = RequestHolder.getTenantId();
  28. return new StringValue(StringUtils.isEmpty(tenantId) ? "" : tenantId);
  29. }
  30. }
  31. @Override
  32. public String getTenantIdColumn() {
  33. return "tenant_id";
  34. }
  35. @Override
  36. public boolean doTableFilter(String tableName) {
  37. return !multiTenantTable.contains(tableName);
  38. }
  39. });
  40. sqlParserList.add(tenantSqlParser);
  41. paginationInterceptor.setSqlParserList(sqlParserList);
  42. paginationInterceptor.setSqlParserFilter(metaObject -> {
  43. if (MultiTenantSqlFilterHelper.getSqlParserInfo(metaObject)) {
  44. return true;
  45. }
  46. return false;
  47. });
  48. }
  49. /**
  50. * 初始化多租户相关表
  51. */
  52. private void initMultiTenantTable() {
  53. multiTenantTable = Sets.newHashSet(multiTenantTables.split(","));
  54. }
  55. @Override
  56. public Object intercept(Invocation invocation) throws Throwable {
  57. return paginationInterceptor.intercept(invocation);
  58. }
  59. @Override
  60. public Object plugin(Object target) {
  61. return paginationInterceptor.plugin(target);
  62. }
  63. @Override
  64. public void setProperties(Properties properties) {
  65. paginationInterceptor.setProperties(properties);
  66. }
  67. }

配置文件

  1. # 多租户表配置 多张表用英文逗号分隔
  2. multi.tenant.table=table1,table2
  3. # 多租户开关
  4. multi.tenant.enable=true

mybatis-plugin.xml新增多租户插件配置

  1. <plugins>
  2. <plugin interceptor="com.fudai.interceptor.MultiTenantPlugin"/>
  3. </plugins>

特殊Mapper方法sql不拼接租户id设计

  • 如果Mapper接口使用的是com.baomidou.mybatisplus.core.mapper,可以直接用mybatisplus自带的注解@SqlParser。否则用以下方法自定义注解做过滤(用的是tk.mybatis.mapper.common.Mapper)。

(1)定义Mapper方法层的sql过滤注解

(Mapper层特定的方法不走多租户加上改注解)

  1. import java.lang.annotation.*;
  2. /**
  3. * @className: SqlFilter
  4. * @description:
  5. * @author: fudai
  6. * @date: 2022-12-14 18:04
  7. */
  8. @Documented
  9. @Retention(RetentionPolicy.RUNTIME)
  10. @Target({ElementType.TYPE, ElementType.METHOD})
  11. public @interface SqlFilter {
  12. boolean filter() default false;
  13. }
(2)新增多租户sql过滤工具类
  1. import com.baomidou.mybatisplus.core.toolkit.StringPool;
  2. import com.wacai.loan.screw.base.annotation.SqlFilter;
  3. import org.apache.ibatis.mapping.MappedStatement;
  4. import org.apache.ibatis.reflection.MetaObject;
  5. import org.springframework.core.annotation.AnnotationUtils;
  6. import java.lang.reflect.Method;
  7. import java.util.Map;
  8. import java.util.concurrent.ConcurrentHashMap;
  9. /**
  10. * @className: MultiTenantSqlFilterHelper
  11. * @description: 多租户sql过滤工具类
  12. * @author: fudai
  13. * @date: 2022-12-14 18:03
  14. */
  15. public class MultiTenantSqlFilterHelper {
  16. public static final String DELEGATE_MAPPED_STATEMENT = "delegate.mappedStatement";
  17. /**
  18. * SQL 解析缓存
  19. * key 可能是 mappedStatement 的 ID,也可能是 class 的 name
  20. */
  21. private static final Map<String, Boolean> SQL_FILTER_INFO_CACHE = new ConcurrentHashMap<>();
  22. /**
  23. * 初始化缓存 Mapper方法上 SqlFilter 注解信息
  24. *
  25. * @param mapperClassName Mapper Class Name
  26. * @param method Method
  27. */
  28. public static void initSqlParserInfoCache(String mapperClassName, Method method) {
  29. SqlFilter sqlFilter = AnnotationUtils.findAnnotation(method,SqlFilter.class);
  30. if (sqlFilter != null) {
  31. if (SQL_FILTER_INFO_CACHE.containsKey(mapperClassName)) {
  32. // mapper 接口上有注解
  33. Boolean value = SQL_FILTER_INFO_CACHE.get(mapperClassName);
  34. if (!value.equals(sqlFilter.filter())) {
  35. // 取反,不属于重复注解,放入缓存
  36. String sid = mapperClassName + StringPool.DOT + method.getName();
  37. SQL_FILTER_INFO_CACHE.putIfAbsent(sid, sqlFilter.filter());
  38. }
  39. } else {
  40. String sid = mapperClassName + StringPool.DOT + method.getName();
  41. SQL_FILTER_INFO_CACHE.putIfAbsent(sid, sqlFilter.filter());
  42. }
  43. }
  44. }
  45. /**
  46. * 获取 SqlFilter 注解信息
  47. *
  48. * @param metaObject 元数据对象
  49. */
  50. public static boolean getSqlParserInfo(MetaObject metaObject) {
  51. String id = getMappedStatement(metaObject).getId();
  52. Boolean value = SQL_FILTER_INFO_CACHE.get(id);
  53. if (value != null) {
  54. return value;
  55. }
  56. String mapperName = id.substring(0, id.lastIndexOf(StringPool.DOT));
  57. return SQL_FILTER_INFO_CACHE.getOrDefault(mapperName, false);
  58. }
  59. /**
  60. * 获取当前执行 MappedStatement
  61. *
  62. * @param metaObject 元对象
  63. */
  64. public static MappedStatement getMappedStatement(MetaObject metaObject) {
  65. return (MappedStatement) metaObject.getValue(DELEGATE_MAPPED_STATEMENT);
  66. }
  67. }
(3)初始化多租户Mapper方法sql过滤注解缓存服务
  1. import com.wacai.loan.screw.base.util.MultiTenantSqlFilterHelper;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.beans.BeansException;
  4. import org.springframework.context.ApplicationContext;
  5. import org.springframework.context.ApplicationContextAware;
  6. import org.springframework.stereotype.Service;
  7. import tk.mybatis.mapper.common.Mapper;
  8. import java.lang.reflect.Method;
  9. import java.util.Map;
  10. /**
  11. * @className: MultiTenantSqlFilterCacheService
  12. * @description: 多租户Mapper方法sql过滤注解缓存服务
  13. * @author: fudai
  14. * @date: 2022-12-14 16:13
  15. */
  16. @Service
  17. @Slf4j
  18. public class MultiTenantSqlFilterCacheService implements ApplicationContextAware {
  19. @Override
  20. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  21. Map<String, Mapper> mapperMap = applicationContext.getBeansOfType(Mapper.class);
  22. mapperMap.forEach((s, mapper) -> {
  23. try {
  24. Method[] methods = mapper.getClass().getMethods();
  25. for (Method method : methods) {
  26. if (!method.isBridge()) {
  27. MultiTenantSqlFilterHelper.initSqlParserInfoCache(applicationContext.getType(s).getName(), method);
  28. }
  29. }
  30. } catch (Exception e) {
  31. log.error("多租户Mapper方法sql过滤注解缓存异常,mapper:{},异常:", s, e);
  32. }
  33. });
  34. }
  35. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/407469
推荐阅读
相关标签
  

闽ICP备14008679号