【MyBatis新手避坑】详解 `Could not find resource ...Mapper.xml` 错误
文章目录
- 一、 问题发现:当满屏红色指向一个“文件找不到”的错误
- 二、 为啥?深入理解 Maven/Gradle 的构建约定
- 三、 咋办?两种解决方案(强烈推荐第一种)
- 方案一:【最佳实践】将 Mapper.xml 文件移至 `resources` 目录
- 方案二:【备用方案】修改 `pom.xml` 配置文件
- 四、 总结
如果你是一位正在学习 MyBatis 的 Java 开发者,那么你很可能在某个深夜,满怀期待地运行第一个测试时,被一盆红色的
ExceptionInInitializerError
冷水浇得透心凉。别灰心,这几乎是每个 MyBatis 新手的“成年礼”。今天,我们就来彻底解剖这个经典的错误,让你不仅知道“咋办”,更明白“为啥”。
一、 问题发现:当满屏红色指向一个“文件找不到”的错误
让我们先回顾一下案发现场。你可能像我一样,精心编写了 StudentDao
接口、StudentMapper.xml
映射文件,以及一个用于获取 SqlSession
的 MyBatisUtils
工具类。然后,当你运行 StudentDaoTest
时,控制台却无情地打印出如下错误:
Exception in thread "main" java.lang.ExceptionInInitializerErrorat com.github.xxx.dao.StudentDaoTest.main(StudentDaoTest.java:16)
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in com/github/xxx/dao/StudentMapper.xml
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/github/xxx/dao/StudentMapper.xml...
Caused by: java.io.IOException: Could not find resource com/github/xxx/dao/StudentMapper.xml...
作为“侦探”,我们应该学会从错误堆栈(Stack Trace)中寻找线索。拨开层层上报的异常迷雾,我们最终会定位到最根本的原因(Root Cause):
java.io.IOException: Could not find resource com/github/xxx/dao/StudentMapper.xml
这句话的潜台词非常直白:“嘿,我(MyBatis)找不到你让我加载的 StudentMapper.xml
这个文件!”
二、 为啥?深入理解 Maven/Gradle 的构建约定
要理解为什么会找不到,我们必须先了解 Java 项目构建工具(如 Maven 或 Gradle)的基本工作原理,这被称为“约定优于配置”(Convention over Configuration)。
在一个标准的 Maven 项目中,目录结构有着明确的约定:
src/main/java
:存放 Java 源代码(.java
文件)。构建时,这个目录下的.java
文件会被编译成.class
文件,并输出到target/classes
目录。src/main/resources
:存放所有资源文件(非.java
文件)。例如.xml
,.properties
等。构建时,这个目录下的所有文件和目录结构会被原封不动地复制到target/classes
目录。
那么,问题的根源就浮出水面了:
我们通常习惯于将 StudentMapper.xml
和它的接口 StudentDao.java
放在同一个包下,也就是 src/main/java/com/github/xxx/dao/
。然而,根据 Maven 的默认约定,它只会编译 java
目录下的 .java
文件,而会忽略掉所有的 .xml
文件。
最终导致的结果是,项目编译打包后,你的 target/classes
目录中只包含了 StudentDao.class
,而 StudentMapper.xml
却“人间蒸发”了。当程序运行时,MyBatis 在 Classpath(也就是 target/classes
)里自然找不到这个至关重要的 XML 文件,于是毫不犹豫地抛出了 IOException
。
三、 咋办?两种解决方案(强烈推荐第一种)
既然找到了病因,对症下药就变得非常简单了。
方案一:【最佳实践】将 Mapper.xml 文件移至 resources
目录
这是最符合 Maven 规范,也是业界通用的标准做法。它能确保你的项目结构清晰,职责分明。
-
定位
resources
目录:在你的项目结构中,找到src/main/resources
。 -
创建镜像目录:在
resources
目录下,手动创建与你的 Dao 接口包名完全相同的目录结构。- 例如,如果你的 Dao 接口在
com.github.xxx.dao
,那么就在resources
下创建com/github/xxx/dao
。
- 例如,如果你的 Dao 接口在
-
移动文件:将
StudentMapper.xml
文件从src/main/java/...
目录剪切并粘贴到新创建的src/main/resources/com/github/xxx/dao/
目录下。
最终,你的项目结构会是这样,清爽又规范:
src
├── main
│ ├── java
│ │ └── com/github/xxx/dao
│ │ └── StudentDao.java # 接口留在这里
│ │
│ └── resources
│ ├── com/github/xxx/dao
│ │ └── StudentMapper.xml # XML文件移到这里
│ │
│ └── mybatis-config.xml # 其他资源文件
│
└── test
- 重新运行:现在,重新运行你的测试代码。Maven 会将
resources
下的所有内容 faithfully 复制到target/classes
,MyBatis 将顺利找到它的映射文件,错误也就随之消失。
方案二:【备用方案】修改 pom.xml
配置文件
如果你有强烈的个人偏好,坚持要将 .java
和 .xml
文件放在一起,也不是不可以。你需要通过修改 pom.xml
文件来覆盖 Maven 的默认行为。
- 打开项目根目录下的
pom.xml
文件。 - 在
<build>
标签内,添加如下<resources>
配置。这相当于告诉 Maven:“除了resources
目录,也请把java
目录下的.xml
文件当作资源文件处理。”
<build><resources><!-- 处理 src/main/java 目录下的 XML 文件 --><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes><filtering>false</filtering></resource><!-- 确保 src/main/resources 的默认行为不受影响 --><resource><directory>src/main/resources</directory><includes><include>**/*.xml</include><include>**/*.properties</include></includes><filtering>true</filtering></resource></resources>
</build>
- 保存
pom.xml
文件,等待 IDE 刷新 Maven 配置后,再次运行即可。
虽然这种方法也能解决问题,但我个人不太推荐。因为它破坏了“约定优于配置”的原则,可能会让其他接手你项目的开发者感到困惑。
四、 总结
编程中的许多错误,看似是代码逻辑问题,实则是对工具链和底层原理理解不够深入。Could not find resource
这个错误就是最好的例子。
- 病因:Maven/Gradle 默认不打包
src/main/java
目录下的资源文件。 - 药方:遵循“约定优于配置”的原则,将资源文件(如
.xml
)放置在src/main/resources
目录下,并保持与 Java 包结构一致的目录结构。
希望这篇博客能帮助你彻底告别这个“新手村”的拦路虎。如果你觉得有帮助,欢迎点赞、收藏和分享!下次遇到问题,记得先从“为什么”开始思考,你会发现编程的世界会因此变得更加清晰。Happy Coding!