MyBatis中resultType 和 resultMap的区别
resultType是MyBatis的自动映射,适用于列名与属性名匹配的简单场景。resultMap是手动映射,通过显式配置处理列名不匹配、一对多等复杂关系,控制力更强。
我们来详细解析一下 MyBatis 中 resultType 和 resultMap 的区别、用法和选择时机。这是 MyBatis 中一个非常核心且重要的概念。
一、 核心思想
一句话概括:
resultType:自动映射。MyBatis 会自动将查询结果的列名与resultType指定的 Java 对象的属性名进行匹配并赋值。它是一种“约定优于配置”的简化模式。resultMap:手动映射。你需要显式地定义一个<resultMap>,在其中详细描述数据库列名与 Java 对象属性名之间的映射关系。它提供了完全的控制权,用于处理复杂或不匹配的场景。
二、 resultType 详解
resultType 用于指定 SQL 查询结果集应该被映射成的 Java 类型。它适用于简单的映射场景。
1. 工作原理
MyBatis 获取到查询结果后,会遍历每一行数据。对于每一行,它会尝试创建一个 resultType 指定类型的实例,然后将列名(或列的别名)与对象的属性名进行匹配。
匹配规则:
- MyBatis 会自动将下划线命名法(如
user_name)的列名转换为驼峰命名法(如userName)的属性名进行匹配。 - 这个自动转换功能需要通过配置开启(在
mybatis-config.xml中):xml<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> - 如果列名和属性名完全一致(忽略大小写),则直接匹配。
2. 适用场景
- 简单对象映射:查询结果的列名和 Java 对象的属性名能够直接对应(或通过驼峰转换对应)。
- 返回基本类型:查询结果只有一列,并且你想将它映射为
int,String,long,boolean等基本类型或包装类型。 - 返回 Map:将查询结果的每一行映射为一个
Map<String, Object>,其中 key 是列名,value 是列值。
3. 示例
数据库表 (user)
| id | user_name | user_age |
|---|---|---|
| 1 | Alice | 25 |
Java POJO (User.java)
public class User {
private Integer id;
private String userName; // 驼峰命名
private Integer userAge; // 驼峰命名
// getters and setters...
}
Mapper XML
<!-- 假设 mybatis-config.xml 中已开启 mapUnderscoreToCamelCase -->
<select id="findUserById" resultType="com.example.model.User">
SELECT id, user_name, user_age FROM user WHERE id = #{id}
</select>
<!-- 返回基本类型 -->
<select id="findUserNameById" resultType="java.lang.String">
SELECT user_name FROM user WHERE id = #{id}
</select>
<!-- 返回 Map -->
<select id="findUserMapById" resultType="map">
SELECT id, user_name, user_age FROM user WHERE id = #{id}
</select>
4. 优缺点
- 优点:配置简单,代码量少,非常直观。
- 缺点:灵活性差,无法处理复杂的映射关系(如嵌套对象、一对多等)。
三、 resultMap 详解
resultMap 是 MyBatis 中最强大、最核心的元素。它让你能够完全掌控结果集的映射过程。
1. 工作原理
你需要先定义一个 <resultMap> 元素,给它一个唯一的 id,并指定它要映射的 Java 类型 (type)。然后在 <resultMap> 内部,使用 <id>, <result>, <association>, <collection> 等子元素来精确定义每一列到每一个属性的映射。
最后,在 <select> 标签中,使用 resultMap 属性引用这个 <resultMap> 的 id。
2. resultMap 的核心子元素
<constructor>:用于构造器注入,将结果注入到类的构造方法中。<id>:定义主键的映射。这对于性能优化和缓存非常重要。<result>:定义普通列到普通属性的映射。column:数据库的列名。property:Java 对象的属性名。javaType:属性的 Java 类型。jdbcType:列的 JDBC 类型。
<association>:处理 “一对一” 关系,将多列表的查询结果映射到一个包含嵌套对象的复杂对象中。<collection>:处理 “一对多” 关系,将查询结果映射到一个包含对象集合(如List)的复杂对象中。<discriminator>:鉴别器,可以根据结果集中某个列的值,来决定使用哪一套映射规则。类似于 Java 中的switch语句。
3. 适用场景
- 列名与属性名不匹配:当数据库列名和 Java 属性名没有任何规律,无法通过驼峰转换匹配时。
- 复杂的连接查询:需要将多个表连接查询的结果映射到一个或多个嵌套的 Java 对象中(一对一、一对多)。
- 高级映射:需要使用到鉴别器、复合主键等高级功能。
- 提升代码可读性:当 SQL 中使用了大量别名时,使用
resultMap可以让映射关系在 XML 中一目了然,而不是散落在 SQL 语句中。
4. 示例
场景:一个博客 (Blog) 有多篇文章 (Post)
数据库表 (blog, post)
CREATE TABLE blog (
blog_id INT PRIMARY KEY,
blog_title VARCHAR(255)
);
CREATE TABLE post (
post_id INT PRIMARY KEY,
post_title VARCHAR(255),
b_id INT -- 外键,关联 blog.blog_id
);
Java POJO (Blog.java, Post.java)
// Blog.java
public class Blog {
private Integer blogId;
private String blogTitle;
private List<Post> posts; // 一对多关系
// getters and setters...
}
// Post.java
public class Post {
private Integer postId;
private String postTitle;
// getters and setters...
}
Mapper XML
<!-- 1. 定义一个 ResultMap -->
<resultMap id="BlogWithPostsResultMap" type="com.example.model.Blog">
<!-- 主键映射 -->
<id property="blogId" column="blog_id"/>
<!-- 普通属性映射 -->
<result property="blogTitle" column="blog_title"/>
<!-- 集合(一对多)映射 -->
<collection property="posts" ofType="com.example.model.Post">
<id property="postId" column="post_id"/>
<result property="postTitle" column="post_title"/>
</collection>
</resultMap>
<!-- 2. 在 select 语句中使用 resultMap -->
<select id="findBlogWithPosts" resultMap="BlogWithPostsResultMap">
SELECT
b.blog_id,
b.blog_title,
p.post_id,
p.post_title
FROM
blog b
LEFT JOIN
post p ON b.blog_id = p.b_id
WHERE
b.blog_id = #{id}
</select>
在这个例子中,resultType 根本无法实现将查询结果自动映射成一个 Blog 对象,并且该对象内部还包含一个 List<Post>。只有 resultMap 才能完成这种复杂的结构化映射。
5. 优缺点
- 优点:功能强大,控制力强,可以处理任何复杂的映射关系,结构清晰。
- 缺点:配置相对繁琐,代码量比
resultType多。
四、 对比总结与选择建议
| 特性 | resultType (自动映射) |
resultMap (手动映射) |
|---|---|---|
| 核心机制 | 约定优于配置,自动匹配 | 显式声明,精确控制 |
| 使用方式 | resultType="com.example.User" |
resultMap="userResultMap" |
| 处理能力 | 简单对象、基本类型、Map | 任何复杂对象,包括嵌套和集合 |
| 关系映射 | 不支持(一对一、一对多) | 完美支持(通过 <association> 和 <collection>) |
| 列名/属性名 | 要求匹配或符合驼峰转换规则 | 无要求,可任意指定映射关系 |
| 配置复杂度 | 非常简单,一行搞定 | 相对复杂,需要定义 <resultMap> 块 |
| 代码可读性 | 映射关系隐含在命名约定中 | 映射关系在 XML 中清晰可见,一目了然 |
| 性能 | 对于简单映射,性能极好 | 对于复杂映射,提供了 <id> 等优化手段,性能可控 |
选择建议
首选
resultType:- 当你进行单表查询,且数据库列名与 Java 类的属性名遵循统一规范时(例如,都使用下划线,并在 MyBatis 中开启驼峰转换)。
- 当你的查询结果只是一个简单的值(如
count(*))或一个简单的对象时。 - 原则:能用
resultType解决的,就不要用resultMap,保持简洁。
必须使用
resultMap:- 当你的查询是多表连接查询,需要将结果映射到包含嵌套对象或对象集合的复杂 Java 对象时(这是最常见的
resultMap使用场景)。 - 当数据库列名和 Java 属性名完全不一致,且你不想在 SQL 中使用大量
AS别名来重命名列时。 - 当你需要利用 MyBatis 的高级特性,如鉴别器 (
discriminator)、延迟加载 (Lazy Loading) 等。 - 原则:当
resultType无法满足你的映射需求时,resultMap就是你的终极解决方案。
- 当你的查询是多表连接查询,需要将结果映射到包含嵌套对象或对象集合的复杂 Java 对象时(这是最常见的
总之,resultType 和 resultMap 是 MyBatis 为不同复杂度的场景提供的两种映射方案。理解它们的区别并根据实际情况做出正确选择,是高效使用 MyBatis 的关键。