引言
众所周知,我们完成一个项目的时间三分之一是用来写代码的,而剩下三分之二的时间是用来调试寻找解决bug的。
那么如果能提高解决bug的效率就能大大加快项目的开发速度。
下面我以mybatis框架报的Mapped Statements collection does not contain value for xxx的异常为例,来说说如何有目的,有效率地解决bug。
环境:mysql+mybatis框架+java编写的maven项目
想看具体项目的可以去看我项目实战专栏里的图书信息管理系统(一个适合刚入行新手的练手项目)
链接:https://blog.csdn.net/qq_46101869/article/details/106910497
案例
好了回归正题,
上图是我在调试遇到的一个问题,可以看到程序报了Mapped Statements collection does not contain value for xxx的异常,这很明显是mybatis框架报的异常,通过报错信息大概猜测是mybatis XXX容器内不包含我写的Mapper(因为那时候我还不知道Mapped Statement是什么东西),然后我就无脑将这段报错信息贴到百度上搜,确实有很多博客记录了此错误及解决方法,我截了一个下来,如图:
但实际上我按照博客上一个个去做,并没有解决问题,这时候我已经花了一个下午时间去查找,问题没解决,倒是把mybatis框架复习了一遍。
苦思之下,我开始逐步调试,以下是我的思考过程:
因为问题肯定出在mybatis框架上,所以我逐步调试,但是呢,我又不懂mybatis源码,看得云里雾里。不过我之前自学Java的时候,跟着视频写过一个类似mybatis的框架------SORM框架(不过功能肯定没mybatis框架复杂,是个小型版的框架),做完后学了其他知识后,自己又回头帮它迭代优化了一下,增加了新的功能,优化了结构。
这段经历让我能大概理解mybatis框架的一些行为,比如在这个地方我就注意到了mappedStatement对象size为0。这时我就猜测这应该是框架本身并没有读取到我写的sql语句,那是由什么造成的呢?
这时候我就开始测试,不用接口类的方式(因为创建实体类也是mybatis框架底层做的),为了缩小问题的范围,我们采用原始的方式(但不是原生jdbc),发现还是这个错误,然后我开始怀疑mapper注册问题
这里我原本是采用包扫描的方式注册,然后我开始尝试用指定路径文件方式去注册
然后异常变了,说找不到这个文件
好家伙,之前包扫描的时候报的是Mapped Statements collection does not contain value for xxx,现在直接报没找到这个文件!
这时候我就开始思考为什么?
为什么我用包扫描的方式就不报错呢?而用具体的文件路径就报错呢?
真的是包扫描时找到了xml文件而具体文件路径没找到吗?
不对,不是这样的,换个角度讲,包扫描没扫描到,会报错吗?不会,那问题区间缩小,很可能就是因为xml文件路径的问题。而其他配置文件是找到了的,不然它根本不会提示找不到(路径是写在mybatis-config.xml文件里的),既然我们确定了问题所在,这时候我们就需要尝试改变路径
这时候我再去查博客,搜索的不是异常信息,而是配置文件的路径该怎么写?
在搜索的过程中我逐渐意识到我的项目结构可能与别人不同,所以我在搜索时加了Maven限定词,好家伙,不搜不知道,一搜我找到了原因所在。
原来Maven项目编译时会把文件全都输出到Target文件夹下面
而默认情况下配置文件只会把resource文件夹下面的配置文件输出,这就造成Java文件夹下面的Mapper文件根本不会输出到target里,这样当然就找不到了,于是我修改了Maven项目中核心配置文件pom.xml信息,加入了下面的配置
然后呢?
还是找不到…
本着不抛弃不放弃的精神,我开始关注target文件夹的文件结构
什么,居然有两个com.dreamchser,这是为什么呢?
然后我开始测试加百度,然后发现了IDEA的神坑之处------当我们创建一个包时,com.dreamchser和com/dreamchaser是不同的!
com.dreamchaser就是指第二个圈里的包,com/dreamchaser指的是第一个圈里的包
.和/的差别真的是坑死我我了!
我仔细思考了下,之前查询博客的时候,确实有博客提到idea中创建包时/和.是不一样,但当时我以为我的mapper是被读取进去了,所以没在意,只是检查了其他部分,知道后面调试运行底层源码时MappedStatement这个对象的size=0,通过字面意思猜测mybatis实际上是没有读取进去的,进而开始了这方面的排查,最终找到了原因。
如果用使用动态代理改造CRUD的方式,用接口实现,这意味着接口路径要和xml中那么namespace中的值一致,而在mybatis配置中mapper注册的时候路径要写的是被打包进target/classes下的路径,注意.和/ 的区别
总结
当我们遇到一个bug的时候,不要二话不说就把异常信息Ctrl+C,Ctrl+V去百度。这确实可能会让你解决问题,但是有很大几率是你搜遍了网上的解决方式也没有解决,因为通常一个框架的同一个异常其实是有很多原因,你就会像无头苍蝇那样乱转,运气好可能会解决问题,运气不好就会到处碰壁。虽说面向百度编程这句话不假,但是我们更要做的是知道问题出在哪里。
我们遇到bug,遇到异常所需做的第一件事就是思考为什么会报错,去定位问题的所在。
而在如何定位问题呢?这需要你对程序,对这个框架的运行原理有一定的了解,有对判断问题的直觉和思维。
当然我们也可以利用一些技巧,比如看报错的信息(看不懂英文就网上翻译),根据报错信息来猜测问题的原因;还有就是切换思维,我们应该以程序的思维去思考,如果要这么做程序会怎么做?它需要什么条件?什么情况下会报这种错误?当然其实更有效更简单粗暴的做法就是调试,你可以看看它在那个地方停下报错了,看看调试过程中配置是否加载完全了。
然后我们再有目的地去百度,把你思考得到的关键词(或者怀疑可能出错的地方)当成限定词,你这样再去百度的话,大概率会得到你想要的答案,同时这一思考过程也会大大提升你对知识、工具的理解,锻炼你自己的能力,这点也是极为重要的!
欢迎在评论区留下你的意见和建议,我们可以一起探讨技术,共同进步!