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忽略多租户隔离内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java语言实现Blowfish加密算法完整代码分享

    Java语言实现Blowfish加密算法完整代码分享

    这篇文章主要介绍了Java语言实现Blowfish加密算法完整代码分享,简单介绍了blowfish加密算法,具有一定借鉴价值,需要的朋友可以参考下。
    2017-11-11
  • 详解Spring中REQUIRED事务的回滚机制详解

    详解Spring中REQUIRED事务的回滚机制详解

    在Spring的事务管理中,REQUIRED是最常用也是默认的事务传播属性,本文就来详细的介绍一下Spring中REQUIRED事务的回滚机制,感兴趣的可以了解一下
    2025-09-09
  • RocketMQ Broker如何保存消息源码解析

    RocketMQ Broker如何保存消息源码解析

    这篇文章主要为大家介绍了RocketMQ源码分析Broker如何保存消息详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • 详解SpringBoot如何创建自定义Starter

    详解SpringBoot如何创建自定义Starter

    Spring Boot的自动配置机制为开发人员提供了一种轻松集成和配置各种功能的便捷方式,本文将深入探讨在Spring Boot中如何创建自定义Starter,为构建模块化且易维护的应用提供有力的支持,需要的朋友可以参考下
    2024-02-02
  • Java instanceof和getClass()区别实例解析

    Java instanceof和getClass()区别实例解析

    这篇文章主要介绍了Java instanceof和getClass()区别实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • MyBatis中XML映射器的实现

    MyBatis中XML映射器的实现

    MyBatis的真正强大在于它的语句映射,映射器的XML文件就显得相对简单,本文主要介绍了MyBatis中XML映射器的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • SpringBoot基于SpringSecurity表单登录和权限验证的示例

    SpringBoot基于SpringSecurity表单登录和权限验证的示例

    这篇文章主要介绍了SpringBoot基于SpringSecurity表单登录和权限验证的示例。文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • Java单例模式的应用示例

    Java单例模式的应用示例

    这篇文章主要介绍了Java单例模式的应用示例,需要的朋友可以参考下
    2014-02-02
  • Spring如何使用xml创建bean对象

    Spring如何使用xml创建bean对象

    这篇文章主要介绍了Spring如何使用xml创建bean对象,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Mybatis 如何开启控制台打印sql语句

    Mybatis 如何开启控制台打印sql语句

    这篇文章主要介绍了Mybatis 如何开启控制台打印sql语句问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11

最新评论