MyBatis-Plus忽略多租户隔离自定义注解
作者:沸羊羊开发
本文主要介绍了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忽略多租户隔离内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!