首页 > 编程语言 > 解决SpringBoot整合MybatisPlus分模块管理遇到的bug
2021
11-19

解决SpringBoot整合MybatisPlus分模块管理遇到的bug

前言

这个Bug前前后后折腾了两天才找到答案,虽说不是完全两天的工作时间在调试这个问题,但是过程也确实曲折,所以做一下记录,也当做一次自我反省

背景

SpringBoot 与 MyBatis-Plus 的 pom 依赖

<!-- SpringBoot 版本 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- MyBatis-plus 版本 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.1.0</version>
</dependency>

项目分模块结构

1-项目结构

之前一直停留在 SpringBoot 整合其他框架做一些小项目,所以都是把代码放在一个工程,也没有进行分模块管理。

由于公司打算开发新产品,我想尝试在 SpringBoot 的项目来做分模块的开发(Maven工程),并且用 MyBatis-Plus 来做持久层框架,也就是将 SpringBoot 做 web模块, 实体跟Mapper 拆分到 persistence模块,并将 mapper.xml 文件放在 persistence模块 的 resources/mapper 目录下。

在项目分模块完成之后,并进行代码移植时并没有发现问题,因为此时并不需要调用到自定义 mapper.xml 中的 sql 查询数据库。但随着一步一步的完善代码,也开始需要用到 mapper.xml 的 sql 查询,此时,在调试时,发现了如下错误:

即,报找不到对应的 sql

排查原因

排除 mapper.xml 书写错误

首先排除 mapper.xml 中 namespace 或 sql 的 id 写错,因为我的开发工具下载了 MybatisX 的插件,mapper.java 与 mapper.xml 都有插件的小图标,如下图所示,

mapper.java有Mybats的插件小图标 mapper.xml有Mybats的插件小图标

怀疑:分模块导致

其次,在先前我已经整合过 SpringBoot 与 MyBatis-Plus 单项目的工程,并没有这个问题,所以,我怀疑是因为 分模块导致的原因 导致的,所以,我想 分模块跟不分模块的工程 有区别?

区别就是,不分模块 mapper.xml 文件就在项目 classes 下的 mapper 中,而分模块后, mapper.xml 文件就在 persistence模块打出来的 jar 中

错误思路:修改pom文件配置resources目录

所以,发现了这点之后,就开始必应解决方案,但是刚开始我的思路是 错误的,我以为项目打包之后,没有把 persistence模块的 resources 下 的 mapper 目录打包进 jar 中 。浪费了一些时间之后,我才想清楚应该先去 persistence模块 打出来的 jar 中确认有没有将 resources 下的 mapper/mapper.xml 打包进去。

确认persistence模块打出的jar中有没有mapper目录

怀疑:Spring Resource时没有加载 jar 中的 mapper.xml

上面的问题教了自己一课,眼见为实 ,虽然解决 Bug 的过程中,合理的猜测是必要的,但是在有限的结果集中,验证排除一些错误的思路更为重要。就像,经常有人会问我,“为什么我运行老是报找不到类的错误”,我就说那你到 tomcat 的发布路径上看一下 jar 包有没有发不上去,这其实是一个道理的。

回到问题,为什么Spring Resource时没有加载 jar 中的 mapper.xml ?必应寻找解决方案,在必应的过程中我才想起了 Spring 的 Resource 加载文件, classpath 与 classpath* 的区别,好久没有自己手写搭项目,把这个给忘记了。

springboot的yml的mybatisplus配置

所以,此时我确信将 application.xml 中 mybatis-plus.mapper-locations 的值改为 classpath*:mapper/*Mapper.xml 即可,如下

#mapper plus
mybatis-plus:
  mapper-locations: classpath*:mapper/*Mapper.xml

怀疑:SpringBoot 跟 MyBatis-Plus 整合的配置哪里不对吗?

上面的配置修改之后,重新运行,还是报同样的错!!!

What The Fuck!!!因为我觉得这个 bug 就是由于 classpath:mapper/*Mapper.xml Spring 的 Resource 只能扫描当前应用中的 classpath 下的 mapper 目录的 mapper.xml 的文件,所以,我又重新必应了 SpringBoot 与 Mybati-plus 的整合配置 ,说到底还是对这个框架不了解,导致走了不少弯路,而且在出问题的时候,没有坚信自己的判断,而不断的尝试必应出来的结果,使得又浪费了一些时间在这个问题上。

最后必应了一圈后,还是没有找到解决方案,而且与此相关的问题文章也不多, 我开始迷茫跟急躁了,毕竟已经浪费了这么多的时间,我还是决定先验证一下我前面深信的东西---- Spring 的 Resource 加载文件, classpath 与 classpath* 的区别 ,所以,我决定自己创建 sqlSessionFactory ,代码如下

@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
    MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
    sqlSessionFactory.setDataSource(datasource);
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    Resource[] resources = resolver.getResources("classpath*:mapper/*Mapper.xml");
    sqlSessionFactory.setMapperLocations(resources);
    MybatisConfiguration configuration = new MybatisConfiguration();
    configuration.setJdbcTypeForNull(JdbcType.NULL);
    configuration.setMapUnderscoreToCamelCase(true);
    configuration.setCacheEnabled(false);
    sqlSessionFactory.setConfiguration(configuration);
    //添加分页功能
    sqlSessionFactory.setPlugins(new Interceptor[]{
        paginationInterceptor()
    });
    return sqlSessionFactory.getObject();
}

结果是运行通过了!!不过,这也是理所当然的,这就说明了我在 application.xml 中 mybatis-plus 的配置没生效,相当于又回到了上一个问题。到了这里,我才想到去看 mybatis-plus-boot-starter 里面的源码!!

没想到的是,源码相当容易看,首先,jar 包的目录结构如下,看名字直接选择看 MybatisPlusAutoConfiguration 这个配置类

mybatis-plus-boot-starter的源码结构

打开 MybatisPlusAutoConfiguration 的源码,直接看 SqlSessionFactory 的创建,如下,在设置 mapper-locations 的判断处,打下断点,进行调试

MybatisPlusAutoConfiguration的部分源码

并且将前面自定义创建的 SqlSessionFactory 的代码注释掉,application.yml 中配置 mapper-locations: classpath*:mapper/*Mapper.xml ,重新运行,进行源码调试,发现上图断点的逻辑,没有进到 if 的判断中去,调试的 MybatisPlusProperties 对象结果如下,

MybatisPlusProperties中mapper-locations的值

!!!What The Fuck!!!我配置的明明是 classpath*:mapper/*Mapper.xml ,怎么变成了 classpath:mapper/*Mapper.xml 可恨的是,我的思路有绕弯了!!!

怀疑:yml 的配置语法对 classpath*:mapper/*Mapper.xml 解析有问题

我都服了我自己了,但确实是这次修改让我找到了最后的答案,将原本的配置写法改成了数组,写法如下,

#mapper plus
mybatis-plus:
  mapper-locations: [classpath*:mapper/*Mapper.xml]

因为对这种配置格式也没去深究,之前都是一对一的配置项,没有尝试过数组的写法,所以上来就运行报错了,但是报的是 application.yml 的语法格式有误。所以,将值加上引号括起来,变成

#mapper plus
mybatis-plus:
  mapper-locations: ["classpath*:mapper/*Mapper.xml"]

重新运行,发现竟然通过了!!!源码调试的 mapperLocations 的值也是 classpath*:mapper/*Mapper.xml ,此时我以为找到了问题的所在了,就是 yml 的配置语法对 classpath*:mapper/*Mapper.xml 解析有问题 我也对此深信不疑。

结论:开发工具没有同步配置

在没写这笔记前,我还深信问题就是 yml 的语法对值带有 冒号 的解析时,需要对值用 引号 包起来。但是在写笔记做记录的过程中,需要还原场景,在调试的时候,发现并没有说 需要对带有冒号的值加上引号包起来 ,这时我才想起来,会不会是 开发工具的问题,在运行的时候,没有把修改的配置文件同步上去 。经过多次的调试,也证明了这点,有时候项目跑太久,开发工具就会突然出来发难。

搞了半天, 在 application.yml 的配置 classpath*:mapper/*Mapper.xml 是有效的!!!有效的!!!有效的!!! 相当于开发工具的作祟让我白白浪费了2天时间,但是期间也发现了自身的一下问题,太久没有遇到错误,一味的相信搜索引擎而不去思考,最后还差点 用错误的认识,否定了原本正确的东西 。

#mapper plus
mybatis-plus:
  mapper-locations: classpath*:mapper/*Mapper.xml

在发现 application.yml配置修改后没生效的第一时间,我就应该检查发布路径的文件,或者去调试源码。中间浪费太多的时间陷入盲目的必应搜索答案,做笔记反省一下自己,安定就会退步,坚持学习,永远不要放弃思考。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持自学编程网。

编程技巧