MyBatis-Plus拦截器接口InnerInterceptor失效?因MyBatis缓存机制而踩的一个深坑

14 篇文章 0 订阅
3 篇文章 0 订阅

InnerInterceptor 接口是 MyBatis-Plus 提供的一个拦截器接口,用于实现一些常用的 SQL 处理逻辑。例如某个组件运作在多系统的平台上,不同系统需要隔离,于是可以通过这个拦截器接口,给每一条要执行的sql末尾拼接一个AND systemId = "?"的条件来实现不同系统只能查到对应系统ID的数据。

具体玩法样例可以参考这个文章:

Mybatis-Plus实现拦截器接口InnerInterceptor

而今天项目遇到的一个bug情景如下:
所有的sql原本都要通过拦截器拼接这个校验系统ID的条件,但其中一条sql因为要查整个数据库中的数据是否有重复,所以就要去掉这个拼接的条件进而查询整个库表。这里我们之前已经有代码可以实现了,简单来说就是在拦截器里加个if,只有在满足if条件后才去拼接条件sql。
然而复制了之前能跳过拦截器的方法去开发这次的新需求,却非常意外的没能生效,sql语句还是把校验systemId的条件给拼接进去了。打了断点也摸不着头脑,明明代码是一模一样的呀,接收到的参数也是满足跳过拦截条件的,为什么还是会被拦截了呢?

在这里插入图片描述

其实标题已经算剧透了,大伙心里应该都有数了吧?那就来详细说说找出问题的步骤:


先是把断点打到了selectOne前后,发现日志没打印sql,然而依然获取到了查询结果的对象,这是怎么一回事?

继而把断点打到拦截器里,发现在执行这条mybatis-plus的方法时根本就没进拦截器,断点没捕获到。

此时就有很多种猜测了,甚至还猜想过会不会和service层加了事务有关

不过最终的正解是由于mybatis的缓存机制,因为我在该方法里执行这条查询sql前已经执行过一遍相同的sql了(目的是先查询一遍加了systemId校验的,再查一遍全表的),所以mybatis的缓存机制生效,直接将已缓存的sql包括结果直接返回了,所以压根就没执行这条想查全表的sql,故而别说拦截器里的if是否判断了,就连进都没进来,这就是拦截器里断点没捕获到的原因。


解决方法:

1、配置文件中加上
# properties

mybatis.configuration.local-cache-scope=statement
# yaml

mybatis:
  configuration:
    local-cache-scope: statement

关闭mybatis的一级缓存

ps:金融行业不适合使用这个一级缓存,容易出现数据不一致,也没提升多少性能

2、在不能关闭一级缓存的情况下,重新写一条结果一样但内容不一样的sql

sql有差别就不会走缓存机制了。比如你之前的sql是用mybatis生成的,这里就直接用Mapper手写一条


THX!

`InnerInterceptor` 这个术语并不直接出现在标准的软件工程或设计模式理论中,因此它可能是指特定框架、库或编程环境内的一个自定义概念或者是某个特定上下文下的特有名词。 通常,在 Java 编程中,我们谈论 `inner interceptor` 可能是在讨论拦截器模式或 AOP (Aspect Oriented Programming) 的实现细节。拦截器模式是一种设计模式,用于在程序执行流程的关键点插入额外的行为。在某些场景下,如 AspectJ 或 Spring AOP 等框架中,你可以定义“拦截器”(interceptors)来拦截并修改方法的行为。 如果 `inner interceptor` 指的是内部拦截器,那么这可能是指在一个包含多层或复合结构的系统中,嵌套或内部使用的拦截器。例如,在一个大型的基于 AOP 的应用程序中,你可能会设计层次化的拦截器链,其中内部拦截器处理更具体的逻辑或操作,并在必要时传递控制到外部拦截器。 ### 实现示例: 假设在 Spring AOP 中有一个简单的例子,展示如何创建和使用内部拦截器: ```java @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*Service.*(..))") public void logAround(JoinPoint joinPoint) { System.out.println("Logging around the method " + joinPoint.getSignature().getName()); // 内部拦截器可以在这里进行特定的操作,比如性能监控等 new InternalLogger(joinPoint).logPerformance(); proceed(); // 继续执行目标方法 } private static class InternalLogger { public void logPerformance() { // 执行内部拦截器的任务,例如记录日志、统计性能指标等 System.out.println("Internal logger performing logging for performance metrics."); } } } ``` 在这个例子中,`LoggingAspect` 定义了一个切面,它在所有 `com.example.service.*Service.*` 方法前执行。`logAround` 方法就是一个拦截器,而 `InternalLogger` 类提供了一个内部拦截器功能,执行额外的业务逻辑,如性能监测。 ### 相关问题: 1. **在哪些情况下应该使用内部拦截器?** - 当需要对核心业务逻辑有深度定制或增强需求时。 - 需要在复杂的依赖关系中管理异步任务、资源分配等复杂状态时。 - 需要精细控制何时、何处进行性能监控或日志记录。 2. **如何优化内部拦截器的性能?** - 确保内部拦截器只执行必要的操作,避免不必要的计算或数据访问。 - 使用轻量级的数据结构和算法减少内存开销和CPU消耗。 - 内部拦截器通常是作为一个更细粒度的操作被执行,它们往往聚焦于特定的局部需求或执行特定的任务。 - 外部拦截器则可能覆盖更大的范围,负责更广泛的功能,例如全局的日志记录或性能监控策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值