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!