MyBatis-Plus忽略多租户隔离自定义注解

 更新时间:2024年12月13日 09:44:28   作者:沸羊羊开发  
本文主要介绍了MyBatis-Plus忽略多租户隔离自定义注解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

微服务项目中由于默认开启了租户隔离,但是有些情况下需要个别方法不启用。

为了实现这个目标自定义了一个忽略租户隔离的注解:

@IgnoreTenant

将他加在方法上即可,例如:

    @IgnoreTenant
    public CrmSmsTemplate getTemplateByCodeAndTenandtId(String code, Integer tenantId) {
        return crmSmsTemplateMapper.selectOne(new QueryWrapper<CrmSmsTemplate>().eq("code", code).eq("tenant_id", tenantId));
    }

实现步骤:

1.首先定义MybatisPlus的配置类创建一个拦截器MybatisPlusInterceptor

将MybatisPlusSaasConfig中的mybatisPlusInterceptor方法修改

@Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
            @Override
            public Expression getTenantId() {
                String tenantId = TenantContext.getTenant();
                //如果通过线程获取租户ID为空,则通过当前请求的request获取租户(shiro排除拦截器的请求会获取不到租户ID)
                if(oConvertUtils.isEmpty(tenantId)){
                    try {
                        tenantId = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
                    } catch (Exception e) {
                        //e.printStackTrace();
                    }
                }
                if(oConvertUtils.isEmpty(tenantId)){
                    tenantId = "0";
                }
                return new LongValue(tenantId);
            }

            @Override
            public String getTenantIdColumn(){
                return TenantConstant.TENANT_ID_TABLE;
            }

            // 返回 true 表示不走租户逻辑
            @Override
            public boolean ignoreTable(String tableName) {
                for(String temp: TENANT_TABLE){
                    if(temp.equalsIgnoreCase(tableName)){
                        if (Objects.nonNull(MybatisTenantContext.get())){
                            return MybatisTenantContext.get();
                        }
                        return false;
                    }
                }
                return true;
            }
            /*@Override
            public boolean ignoreTable(String tableName) {
                if (Objects.nonNull(MybatisTenantContext.get())){
                    return MybatisTenantContext.get();
                }
                return true;
            }*/
        }));
        //update-begin-author:zyf date:20220425 for:【VUEN-606】注入动态表名适配拦截器解决多表名问题
        interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());
        //update-end-author:zyf date:20220425 for:【VUEN-606】注入动态表名适配拦截器解决多表名问题
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        //【jeecg-boot/issues/3847】增加@Version乐观锁支持 
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }

2,定义一个ThreadLocal本地线程变量 MybatisTenantContext用于维护是否开启租户隔离变量

package org.jeecg.config.mybatis;

public class MybatisTenantContext {
    private static final ThreadLocal<Boolean> TENANT_CONTEXT_THREAD_LOCAL = new ThreadLocal<>();

    public static Boolean get() {
        return TENANT_CONTEXT_THREAD_LOCAL.get();
    }

    public static void set(boolean isIgnore){
        TENANT_CONTEXT_THREAD_LOCAL.set(isIgnore);
    }

    public static void clear(){
        TENANT_CONTEXT_THREAD_LOCAL.remove();
    }
}

3.自定义注解

package org.jeecg.config.filter;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface IgnoreTenant {

    /**
     * true为不做租户隔离 false为做租户隔离
     * @return
     */
    boolean isIgnore() default true;
}

4.注解切面类

ps:如果方法或者类上有其他注解用到租户隔离的,如:日志注解,字典翻译注解在point.proceed()后执行逻辑。需要注意切面类的执行顺序,一定要保证TenantIgnoreAspect 先执行,不然其它注解还是会有租户隔离的情况。可以在TenantIgnoreAspect 切面类加上@Order(Integer.MIN_VALUE)注解 保证执行顺序

package org.jeecg.config.filter;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.config.mybatis.MybatisTenantContext;
import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Objects;

@Aspect
@Slf4j
@Component
@Order(Integer.MIN_VALUE)
public class TenantIgnoreAspect {
    /**
     * 切入点
     */
    @Pointcut("@within(org.jeecg.config.filter.IgnoreTenant) ||@annotation(org.jeecg.config.filter.IgnoreTenant)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        try {
            Class<?> targetClass = point.getTarget().getClass();
            IgnoreTenant classIgnoreTenant = targetClass.getAnnotation(IgnoreTenant.class);
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
            IgnoreTenant methodIgnoreTenant = method.getAnnotation(IgnoreTenant.class);

            //判断类上是否有注解
            boolean isClassAnnotated = AnnotationUtils.isAnnotationDeclaredLocally(IgnoreTenant.class, targetClass);
            //判断方法上是否有注解
            boolean isMethodAnnotated = Objects.nonNull(methodIgnoreTenant);

            //如果类上有
            if (isClassAnnotated) {
                MybatisTenantContext.set(classIgnoreTenant.isIgnore());
            }
            //如果方法上有 以方法上的为主
            if (isMethodAnnotated) {
                MybatisTenantContext.set(methodIgnoreTenant.isIgnore());
            }
            Object result = point.proceed();
            return result;
        } finally {
            MybatisTenantContext.clear();
        }
    }
}

 到此为止就可以使用注解:@IgnoreTenant

补充:如果一个方法中有多个查询,但是只有特定查询需要忽略租户隔离,可以使用下面的方式

@Service
public class DemoService {

    public List<String> demoList(String name){
        try {
            MybatisTenantContext.set(true);
            this.listByName(name);
            return this.list();
        }finally {
            MybatisTenantContext.clear();
        }
    }
}

以上代码是手动维护本地线程变量 MybatisTenantContext,不可以使用注解,使用完一定要记得clear。更多相关MyBatis-Plus忽略多租户隔离内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • mybatis配置Mapper.xml文件时遇到的问题及解决

    mybatis配置Mapper.xml文件时遇到的问题及解决

    这篇文章主要介绍了mybatis配置Mapper.xml文件时遇到的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • 基于OpenCV与JVM实现矩阵处理图像

    基于OpenCV与JVM实现矩阵处理图像

    本文主要介绍了Java图像处理实战之基于OpenCV与JVM实现矩阵处理图像。文中的示例代码讲解详细,对我们学习图像处理有一定的帮助,感兴趣的可以试一试
    2022-01-01
  • Java解决通信过程的中文乱码的问题

    Java解决通信过程的中文乱码的问题

    这篇文章主要介绍了 Java解决通信过程的中文乱码的问题的相关资料,需要的朋友可以参考下
    2017-01-01
  • Mybatis Plus 中的LambdaQueryWrapper示例详解

    Mybatis Plus 中的LambdaQueryWrapper示例详解

    这篇文章主要介绍了Mybatis Plus 中的LambdaQueryWrapper,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • Java stream sorted使用 Comparator 进行多字段排序的方法

    Java stream sorted使用 Comparator 进行多字段排序的方法

    这篇文章主要介绍了Java stream sorted使用 Comparator 进行多字段排序,主要讲解使用Java Stream流排序器Comparator对List集合进行多字段排序的方法,包括复杂实体对象多字段升降序排序方法,需要的朋友可以参考下
    2023-03-03
  • springboot实现图片大小压缩功能

    springboot实现图片大小压缩功能

    这篇文章主要为大家详细介绍了springboot实现图片大小压缩功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • Java线程启动为什么要用start()而不是run()?

    Java线程启动为什么要用start()而不是run()?

    这篇文章主要介绍了线程启动为什么要用start()而不是run()?下面文章围绕start()与run()的相关资料展开详细内容,具有一定的参考价值,西药的小火熬版可以参考一下,希望对你有所帮助
    2021-12-12
  • springmvc 防止表单重复提交的两种方法

    springmvc 防止表单重复提交的两种方法

    最近在本地开发测试的时候,遇到一个表单重复提交的现象。本文主要介绍了springmvc 防止表单重复提交的两种方法,感兴趣的可以了解一下
    2021-08-08
  • java容器类知识点详细总结

    java容器类知识点详细总结

    这篇文章主要介绍了java容器类知识点详细总结,
    2019-06-06
  • Java基于jdbc连接mysql数据库操作示例

    Java基于jdbc连接mysql数据库操作示例

    这篇文章主要介绍了Java基于jdbc连接mysql数据库操作,结合完整实例形式分析了java使用jdbc连接mysql数据库的具体步骤与相关注意事项,需要的朋友可以参考下
    2017-07-07

最新评论