一、背景描述

项目要求update/delete必须要有where条件(因为出了一次生产上把一张表的数据全表删除的严重生产事故),并且要打印出where中的条件,所以考虑用mybatis拦截器处理

mybatis拦截器实现原理简述

在Mybatis中,拦截器可拦截如上图中四种相关操作类的操作方法。通过阅读源码可知,执行顺序为: Executor -> StatementHandler -> ParameterHandler -> StatementHandler -> ResultSetHandler

其中:StatementHandler类中包含针对query、update操作的具体拦截方法。因此,拦截基于StatementHandler类进行。

删除或更新拦截器SafeDeleteOrUpdateInterceptor部分代码

在加这个拦截器前,项目中还有一个针对select特殊字符处理的StatementHandler拦截器-MySqlInterceptor

执行sql时报错

 具体是在执行findById方法时报错,findById最后是调用到mybatis的selectOne方法报错。奇怪的是我本地在eclipse启动项目不会有这个报错,打成jar包就有这个报错?

二、开始排查

看报错信息,一开始没有什么头绪,因为是第一次遇到这种错,也是做各种尝试,先是在网上查了这个报错,大部分说是jar包冲突

2.1检查jar包冲突

先看项目中有没有mybatis的包冲突,发现没有

 因为delete/update拦截器用到了github的jsqlparse,于是再检查jsqlparse的包有没有冲突,发现也没有

2.2既然没有jar包冲突,那应该是代码问题, 因为我本地不会报错不好调试,但是看报错信息应该是拦截器那里导致了问题,于是考虑分别注释掉其中一个拦截器试试

注释原来的MySqlInterceptor留下SafeDeleteOrUpdateInterceptor,没有报错,说明SafeDeleteOrUpdateInterceptor本身没什么问题

注释SafeDeleteOrUpdateInterceptor留下原来的MySqlInterceptor,也没有报错

 但是两个拦截器就会报错,然后去github上搜了下,找到一篇相关文章,说的是获取StatementHandler对象时的问题,于是考虑两个拦截器获取StatementHandler对象有什么不一样

MySqlInterceptor是直接强转

 SafeDeleteOrUpdateInterceptor是参照mybaits-plus PluginUtils的写法

既然mybatis-plus是如上的写法,于是考虑把MySqlInterceptor获取StatementHandler对象改成一样的。本地调试发现,确实存在invocation的target还是代理对象,因此需要进一步获取具体的对象

在只有其中的某一个拦截器时,invocation的target就是StatementHandler对象

 三、总结和思考

为什么同种对象如果存在多种拦截器对象时会出现invocation的target还是代理对象的情况,具体我也不是太清楚,没有具体研究源码,但是根据mybatis拦截器机制猜测,如果同种对象存在多种拦截器时,首先有个执行顺序,

四、为什么我本地不报错

原因尚不清楚

五、两个拦截器的执行顺序

在第3点中简要说到同种对象如果存在多种拦截器顺序问题,可参考同行总结的内容

 

 本地验证(gif动图)

plugin加载顺序验证

结果是先执行SafeDeleteOrUpdateInterceptor,再执行MySqlInterceptor。加载顺序的话应该就是先加载MySqlInterceptor再加载SafeDeleteOrUpdateInterceptor

结尾:如有不足,还请大家多多指出

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐