很开心,在使用mybatis的过程中我踩到一个坑。

这是why技术的第14篇原创文章

在实际开发过程中我踩到了mybatis的一个坑,我觉得值得记录、分享一下。

先说说这个坑是什么吧。如果你踩过这个坑,并且知道具体的原因,那这篇文章可以加深你的印象。如果你没有踩过,那你可得好好看看,因为你总会遇到的。

具体如下:在mybatis中的OgnlOps.equal(0,"")返回的是true。

很开心,在使用mybatis的过程中我踩到一个坑。

首先这里返回为true就违背了我们的常识,其次返回为true,会带来什么问题呢?

看完本文你就清楚了。

本文会按照遇到问题 --> 分析问题 --> 解决问题的行文思路,用追踪源码的方法,对这个问题进行剖析。

同时分享一下我是怎么用逆向排查的方法,通过Debug模式找到最关键的那一行源码,然后明白前因后果,最后解决这个问题的。

本文源码:mybatis 3.5.3版本。

背景介绍,需求分析

先铺垫一下背景,模拟一个需求。

有一个订单表,表结构如下:

很开心,在使用mybatis的过程中我踩到一个坑。

为了简化问题,我们假设表里面只有两条数据:

很开心,在使用mybatis的过程中我踩到一个坑。

订单号为1234的订单状态为0【关闭】

订单号为4321的订单状态为1【开启】

已经开发好的功能是模糊查询订单名称,接口如下:

很开心,在使用mybatis的过程中我踩到一个坑。

其对应的mapper.xml是这样写的,功能正常:

很开心,在使用mybatis的过程中我踩到一个坑。

现在需要在已有功能上添加一个根据状态过滤订单的功能:

很开心,在使用mybatis的过程中我踩到一个坑。

假设某个页面有这样的一个下拉框,可以根据订单状态过滤订单数据。

当用户选择【已支付】时,后台接收到的是数字1,用Byte类型接收。

当用户选择【未支付】时,后台接收到的是数字0,用Byte类型接收。

准备开发

现在明确了需求,根据订单状态进行过滤。

很简单,最主要的修改地方就是对mapper.xml的修改,至于怎么从前端传到xml来我就不详细说明了,相信用过mybatis的朋友都知道。

先在接口上加一个入参orderName:

很开心,在使用mybatis的过程中我踩到一个坑。

然后改造一下对应的xml:

很开心,在使用mybatis的过程中我踩到一个坑。

改造点很简单,在xml文件里面ctrl c一下原来的if标签,再ctrl v出来改改里面的名字就好了。

开始自测,遇到问题

请做好单元测试,即使这个功能非常简单,显而易见,你信心十足,但是做好单元测试,是一个程序员应有的职业素养。

单元测试如下:分别传入状态0和1

很开心,在使用mybatis的过程中我踩到一个坑。

按照我们现在表里的数据,我们预期的结果是各自查询出一条数据。

很开心,在使用mybatis的过程中我踩到一个坑。

运行起来,我们一起看看执行结果:

很开心,在使用mybatis的过程中我踩到一个坑。

status=0,查询出来的条数 = 2

status=1,查询出来的条数 = 1

这结果和我们预期的不符呀!什么情况?

很开心,在使用mybatis的过程中我踩到一个坑。

当时我遇到这个问题的时候,我就知道事情不简单,其中必有蹊跷。

如果是两年前,我遇到问题肯定是立马面向搜索引擎编程。把遇到的问题一顿搜索,根据网友的建议,很快就很解决了。然而,也很快就忘记了。而且,遇到这个问题的时候,我当时是没有联网的。

不要急着去问搜索引擎。不要慌,要分析,冷静分析之后才有收获。

分析问题

分析的第一步其实很容易想到,我们先把sql打印出来,看看最终执行的sql是什么,就知道为什么返回的结果和预期不符了。

所以我们在application.properties里面加上这行配置:logging.level.com.xxxx.xxxx.mapper = debug注:上面的xxxx换成自己的mapper包的路径

很开心,在使用mybatis的过程中我踩到一个坑。

加上sql打印后,我们发现当status为0时,mybatis并没有给我们拼接where关键字。

到这里很自然的就能联想到下一步:为什么mybatis没有给我们拼接where关键字?

或者换一个问法:mybatis是在哪里通过上什么逻辑拼接sql的?

常规的方法是加断点进行追踪,但是我想分享一个我当时排查的"骚"操作,定位问题非常快。那就是逆向排查。

逆向排查法

现在我们确定了是sql拼接的问题,我通过日志,也拿到了完整的sql。

日志配置是这样的:logging.pattern.console=