mybatis-plus加载多个module的mapper踩坑记录:Invalid bound statement (not found)
背景

有一个多模块的项目,每个模块中都有自己的mapper.xml文件。但是在执行一次SQL查询中,mybatis却报出了下面的异常

排查过程
第一步,先检查mapper扫描是否正确
先找到这个方法的位置

可以看到包名是com.pinming.security.responsibility.mapper
检查SpringBoot启动类的注解

用通配符的方式匹配路径,可以看到这个写法没有任何问题
然后我又执行了别的mapper类的查询,发现除了第一个module下的两个mapper的自定义查询方法能够被成功映射,其他mapper的方法都会报出上述异常,可以断定确实是mybatis没有扫描到全部的mapper.xml
为了知道是哪里出了问题,我们直接开始debug源码,探究mybatis扫描mapper.xml的原理
根据报错的堆栈信息,我们定位到了报错的最终位置

代码为什么会进入到这里?
我们先来看这里的判断逻辑:ms == null,且方法上不带Flush这个注解,程序就会进行到我们报错的位置
首先思考,Flush这个注解是什么?不知道,也没用过,而且印象中mapper的方法不需要这个注解也能注册上去,所以问题一定出在ms == null的问题上
那么ms是什么?怎么来的?再往上看,它的类型是MappedStatement,逐词翻译就是“映射的语句”,通过resolveMappedStatement方法得到,那我们将断点加到这里,重新执行代码

进入方法内部

发现最后因为configuration.hasStatement(statementId) = false且mapperInterface.equals(declaringClass) = true,最后返回了null
后一个判断分支判断的是接口名称是否匹配,这里一定会匹配,前一个判断分支判断configuration是否有这个语句,很显然没找到,那么继续debug进入前一个语句


这里我debug到buildAllStatements方法里看了半天,发现问题不在这里,就不赘述了
这里我们发现,所有语句被映射在了mappedStatements里

找到它被赋值的地方,我们就能知道mapper中我们自定义的方法是在哪里被映射的


它有唯一的put入口,将断点加在这里,我们重启程序,观察映射的过程

要注意这里要给断点加一个condition,ms.getId()就是方法的全路径,只看自己加的方法是怎么被映射的,因为mybatis-plus的baseMapper有很多内置的方法是通过其他方法初始化的,不要去管那些

我们根据这个堆栈列表,一点点往回看
此处省略几十分钟,中间走错了很多路。最终我们找到了这里

遍历mapperLocations,解析每一个xml,正常来说mapperLocations肯定会包含每个mapper.xml,那我们看看它有哪些值

居然只有meeting这个模块下的两个mapper.xml?为什么?
我们往上找一下这个mapperLocations是怎么来的



用同样的方法,找到了它唯一被赋值的地方,打下断点,重启程序。感觉离真相越来越近了哈哈哈!

进入断点,同样通过左下角的堆栈信息不断往回找

这回只往上找了一步就发现了加载它的地方
再把断点打在properties.resolveMapperLocations()这行,重启!
下面贴上我debug操作的每一步,红框就是我debug进入的方法体




这里是什么?一脸懵逼,我先看看最后会进到哪个分支

再进去

这里看到,spring根据我们配置的mapper.xml路径,开始搜索所有匹配的文件
走到这里不用往下看了,因为这往下的搜索都是根据locationPattern这个入参来决定的,问题一定出在locationPattern上
确定了之后,我们再往回一步

到这里,我们仔细阅读代码,发现一个关键的词语:all
通过猜测,我们得通过调整locationPattern的值,让代码进入上面的分支,继续往前看看locationPattern是怎么来的

在这里,遍历mapperLocations得到下面的入参locationPattern

这个mapperLocations是一个成员变量,并且我们发现MybatisPlusProperties这个类是通过配置文件注入的

在配置文件中搜索mybatis-plus

破案了!就是这个值!
那我们要改成什么值呢?回到前面的判断分支


可见,我们的配置必须以classpath*:开头
试一试

配置完之后重启,执行方法,成功!
结论
想要多个module中的mapper.xml文件都被加载到,配置文件中mybatis-plus.mapper-locations这一项必须以classpath*:开头,否则就只会加载匹配到的第一个module中的指定目录


浙公网安备 33010602011771号