鸿业道路设计不能读取xml⽂件是什么原因_MyBatis:对不起,这锅我不能背(给年轻的程。。。
前⽂
写代码多年,我⼀直有个习惯,只要是要做的功能模块不是很复杂,⼀般都是上来狂写⼀通代码,等功能做好了,再启动服务测试,哪⾥有问题再改(实话说,单元测试写的也不多)。
⽽不是写完⼀个接⼝或⽅法就测试⼀下,最长的记录应该是连着写4、5天代码,然后⼀把测试通过,那感觉,爽到可以多吃⼀碗饭。
代码路上的滑铁卢
然⽽,就在前两天,我感觉遭遇到了代码⼈⽣的滑铁卢,其实遇到过不只⼀次了,每次滑完铁,再爬起来慢慢就忘了。这次,我把它写下来,这样就不会忘了。
事情是这样的,前两天要对项⽬加个功能。项⽬ ORM 采⽤的是 MyBatis,因为增加了数据库表,所以要对应的⽣成 DAO 层和MyBatis 映射⽂件(l)。由于对之前业务不是熟悉,我只是先把各个实体类啊、业务类啊、映射⽂件啊、枚举类啊等等都建出来,然后写了两个简单接⼝准备调试⼀下,于是我点了启动按钮,没问题,没有⼀点错误,项⽬正常启动了,看上去是那么的完美。
我构造了⼀个请求,打算测⼀下刚刚写好的接⼝,当请求发送出去之后,⼀个熟悉的异常出现在了 IDEA 控制台中,invalid bound statement (not found),⽤过 MyBatis 的同学恐怕没有不认识这个异常的,它的意思就是我们调⽤ DAO ⽅法的时候,在 l ⽂件中没有到对应的 statement,或者说是没有到你定义的 SQL 查询语句块。
出现这个异常可能是下⾯的这⼏个原因:
1. xml ⽂件的 namespace 和对应的接⼝名不⼀致
2. 接⼝类中的⽅法和 xml ⽂件中的 statement id 对应不上
datasource是什么意思3. xml ⽂件中有中⽂注释
4. 随意在 xml ⽂件中加⼀个空格或者空⾏然后保存,可能能解决问题
如果你是⽤⼯具⾃动⽣成 xml 还好,如果是⼿动创建的,那很可能由于疏忽出现这个问题,⽐如我们从另⼀个⽂件复制过来,忘记改namespace 了,或者接⼝⽅法名和 statement id 差了⼀个字母或者字母顺序不⼀致。这个异常是很令⼈头疼的,就⽐如相差⼀个字母这种情况,很难被发现,所以最好还是写好接⼝⽅法名,然后复制到 xml 中。
我虽然有段时间没有碰 MyBatis 了,作为⼀个⽼司机,我碰到这个问题其实⼀点也不慌,因为虽然是⼯具⾃动⽣成的 xml ⽂件,但是我确实⼜加了⼏个 statement 块⼉,⽽且 id 也是⼿敲的,并且报错的确实也是我⼿动加上的,所以,我猜测应该是名字没对上,敲错字母或者顺序不⼀致,于是我进去排查了⼀下,但是没发现什么问题,为了保险起见,我⼜到接⼝中把⽅法名字复制到 xml 中了,然后确定namespace 没问题,没有中⽂注释,并且⼜在 xml 中加了个空⾏(虽然从来没⽤这个⽅法解决过问题),然后重新启动项⽬,但是,异常还是没有消失。
及时跳出来,不要陷在⾥⾯
这就有点奇怪了,⼜重新检查了⼀遍,没错,都正常,看不出问题所在。
**当确定没有问题的时候,就要跳出来了,得从其他⽅向或者更⾼层次考虑⼀下了,不然很可能就陷在⾥⾯了。
**划重点,这是多次教训总结出来的规律。我可以确定当前调⽤的这个接⼝⽅法和 statement 都完全没有问题,那很有可能是别的问题,会不会是这个 xml ⽂件没有被编译打包进去,于是我进到 target ⽬录查探⼀番,有的,⽽且查看内容,确定是没有问题的。
有时候问题很奇怪,可能和 IDE 有关,于是我⽤ mvn clean 命令清理了⼀下,然后重新运⾏,但是,问题依旧在。
接下来,我⼜试了删除这个 xml ,然后新建了⼀个,但是,问题依旧。
再往外跳,你不是这个⽅法有问题吗, 那我再新建⼀个⽅法,就写⼀条最简单的 SQL,⽅法名也起的简单⼀点,看看会不会有问题,结果,发现新⼤陆了,这个新建的⽅法也报这个错误。那就有了新的排查⽅向了,我再试试别的接⼝中的⽅法呢,结果,这个包名下的⼏个⽅法,全都有这个错误,⽽其他包名下的⽅法则没有问题,因为不同功能的 xml ⽂件放在不同的包下,也就是不同的路径下。
那我就知道了,是 xml ⽂件扫描出问题了,肯定是 MyBatis 配置的 mapperLocations 有问题了,有可能是被我或者其他同事不⼩⼼多敲了个字母之类的。于是打开配置⽂件看了⼀下,
mybatis:mapperLocations: com/xxx/aaa/mapper/*.xml,com/xxx/aaa/bbb/mapper/*.xml,com/xxx/aaa/ccc/mapper/*.xml
MyBatis 配置 mapperLocations 配置了三个包路径,也就是从这三个包中寻 *.xml去解析,但是经过检查发现,并没有问题,配置⽂件没有 git 提交记录,⽽且配置的包路径也是正确⽆误的,其他两个包都扫描正常,就是 com/xxx/aaa/ccc/mapper/*.xml这个包有问题。于是我⼜试了如下⼏个⽅法:
1. 把这个有问题的包路径放到第⼀个,⽆效。
2. 把其他两个注释,只留这个有问题的,⽆效。
3. 难道是 MyBatis 读取了其他地⽅的配置?于是我把这个配置注释掉,结果都出问题了,说明就是读的这个配置。
源码⼤法好
此时,已经过去很长时间了,问题变得越来越诡异,但是事出必有因,肯定是某些地⽅出现了问题。实在不出项⽬本⾝的问题了,没办法,我只能怀疑是 MyBatis 有问题了,也许真的是触发了 MyBatis 本⾝的隐藏 bug。
不到万不得已是不会⽤这种⽅式的,那就是调试 MyBatis 源码。想来,MyBatis 源码我还是⽐较熟悉的。那咱们就再会⼀会吧。
mybatis-spring-boot-starter 只有三个 Java ⽂件,其中 MybatisAutoConfiguration是关键业务类。
⽽我们知道 MyBatis 中 SqlSessionFactory 是⾮常核⼼的对象,所以我们就把断点加在 sqlSessionFactory(DataSource dataSource)这个⽅法上。
如果是第⼀次调试开源框架源码,往往不能⼀下⼦准位置,其实没有关系,把断点打在任何⼀个位置都可以,⼤不了就慢慢跟两遍
嘛,本⾝读源码、调试的过程就是个漫长的过程。
@Bean@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {    SqlSessionFactoryBean factory =
以上代码我只保留了本次问题相关的代码,那就是解析 mapperLocations 的过程,也就是上⾯代码中
solveMapperLocations()这个⽅法。
public Resource[] resolveMapperLocations() {    ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();    List resources = n
当我继续跟踪代码的时候,发现 MyBatis 确实已经识别到了配置⽂件中的那三个包路径,this.mapperLocations就是那三个包路径的数组
集合。
接着往下跟,在⽅法 Resources(mapperLocation)中对每⼀个路径进⾏解析,发现前两个包都正常返回了
Resource[],也就是对应的 xml ⽂件资源,⽽最后⼀个返回的确实空数组,问题原因已经很近了。
接着再次启动调试,当解析最后⼀个包路径是,进⼊Resources(mapperLocation)⽅法内部,看看⾥⾯都⼲了什
么,最后发现在调⽤以下代码之后,返回的 rootDirURL 是⼀个绝对路径,也就是 xml 所在的物理路径。
URL rootDirURL = URL();
这时,终于发现问题所在了,这个绝对路径竟然不是 xml 所在的路径,⽽是另外⼀个⼦模块下的路径,经过对⽐发现,原来,⼦模块中被
新建了⼀个名称⼀样的⽂件夹,造成存在两个完全⼀样的包路径,⽽以上代码返回了另⼀个包的绝对路径。于是,联系同事,问清楚这个包
被创建的原因,发现是最近新加的但是已经废弃⽆⽤的,于是删掉解决了问题。
正常项⽬开发中应该可以规避这种问题,模块与模块不应该出现相同包名,应该遵循如下命名:
模块A:duleA
模块B: duleB
这样从根本上解决问题,以防出现不必要的⿇烦。
最后
MyBatis 的这个异常确实令⼈头疼,因为错误原因不明显,以此类推,凡是 xml ⽂件造成的问题都不太容易排查,⼤部分情况都是⼈为疏
忽造成的,⽽错误⼀般都⽐较隐蔽。
当⼀个问题经过多⽅验证都没办法被发现被解决的时候,往往就需要换个思路了,及时跳出来,从其它⾓度或者更⾼层次重新审视问题,也
许能更快的到问题原因。
在⽤开源框架的时候,如果出现问题,长时间不到解决办法,那么可以尝试调试⼀下源码,并没有想象的那么困难。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。