三条铁律(缺一不可):
1.📌 同包同名:XML映射文件必须与Mapper接口同名,且位于同一包路径下
2. 📌 namespace绑定:XML中namespace
必须为Mapper接口的全限定名
3.📌 ID匹配+返回一致:SQL的id
必须与方法名一致,且返回类型匹配
在MyBatis的世界里,Mapper接口与XML文件的绑定就像精密齿轮的咬合,毫厘之差就会导致整个系统停摆。下面我们深入解析这三条决定成败的黄金法则!
一、同包同名规则:位置决定成败
规则核心:
- XML文件名必须与Mapper接口名完全相同(包括大小写)
- 两者必须位于编译后的同一包路径下
项目结构示例:
src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── mapper
│ │ └── ProductMapper.java# 接口文件
│ └── resources
│ └── com
│ └── example
│ └── mapper
│ └── ProductMapper.xml# XML文件(同名!)
原理深析:MyBatis初始化时,会扫描类路径下所有Mapper
结尾的接口,并自动查找同一包路径下同名XML文件。位置错位是引发Invalid bound statement
异常的元凶!
二、namespace:绑定接口的DNA
规则核心:XML中的namespace
属性必须精确匹配Mapper接口的全限定名
错误示范(引发绑定异常):
<mapper namespace="com.example.dao.ProductDao"> <!-- 错误!与接口名不匹配 -->
正确配置:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.ProductMapper">
<!-- 此处写SQL -->
</mapper>
namespace
的作用相当于XML文件的"身份证",告诉MyBatis:“我是为ProductMapper
接口服务的”。
三、方法名与SQL ID:精准匹配的艺术
1. 方法名 = SQL ID
接口方法名必须与XML中的SQL ID一字不差:
// 接口方法
public interface ProductMapper {
Product selectProductById(Long id);
}
<!-- XML配置 -->
<select id="selectProductById" resultType="com.example.entity.Product">
SELECT * FROM products WHERE id = #{id}
</select>
2. 返回类型严格匹配
方法返回类型 | XML配置 |
---|---|
Product | resultType="com.example.entity.Product" |
List<Product> | resultType="com.example.entity.Product" (注意不是List!) |
int / void | 无需resultType (增删改操作) |
复杂结果集处理:
<!-- 使用resultMap处理复杂映射 -->
<resultMap id="productDetailMap" type="com.example.vo.ProductVO">
<id property="id" column="product_id"/>
<association property="category" javaType="Category">
...
</association>
</resultMap>
<select id="selectProductDetail" resultMap="productDetailMap">
...
</select>
四、常见错误排查指南
1. 绑定异常三连击
// 经典错误信息集锦
org.apache.ibatis.binding.BindingException:
Invalid bound statement (not found):
[com.example.mapper.UserMapper.selectById] // 通常由以下原因导致:
- ❌ XML文件不在正确包路径
- ❌ namespace与接口名不匹配
- ❌ SQL id与方法名不一致
2. 类型转换黑洞
// 返回类型不匹配的典型报错
Could not set property 'id' of 'Product'
with value '1001' Cause: java.lang.IllegalArgumentException
解决方案:
- 检查
resultType
/resultMap
是否匹配接口返回类型 - 验证数据库字段与实体类属性命名一致性
- 复杂类型使用
<resultMap>
显式映射
五、最佳实践:注解与XML的平衡之道
虽然XML是主流,但简单场景可用注解替代:
public interface ProductMapper {
@Select("SELECT * FROM products WHERE id = #{id}")
Product selectById(Long id);
@Insert("INSERT INTO products(name) VALUES(#{name})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(Product product);
}
选择策略:
- ✅ 注解:单表简单操作
- ✅ XML:动态SQL、复杂关联查询
<!-- XML动态SQL示例 -->
<select id="searchProducts" resultType="Product">
SELECT * FROM products
<where>
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="minPrice != null">
AND price >= #{minPrice}
</if>
</where>
</select>
六、终极验证清单
在启动应用前,请逐项核对:
- XML文件名 = 接口名 +
.xml
- XML文件路径 = 接口包路径(
resources
下创建相同包结构) - XML中的
namespace
= 接口全限定名 - SQL的
id
= 接口方法名 - 查询语句有
resultType
/resultMap
- 增删改操作返回
int
或void
遵循这三大规则,MyBatis的绑定机制将如瑞士钟表般精准运行。记住:在MyBatis的世界里,约定大于配置,精确大于随意!