基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

MyBatis中resultType 和 resultMap的区别

知识点图片

resultType是MyBatis的自动映射,适用于列名与属性名匹配的简单场景。resultMap是手动映射,通过显式配置处理列名不匹配、一对多等复杂关系,控制力更强。

我们来详细解析一下 MyBatis 中 resultTyperesultMap 的区别、用法和选择时机。这是 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)

java
public class User {
    private Integer id;
    private String userName; // 驼峰命名
    private Integer userAge;  // 驼峰命名
    // getters and setters...
}

Mapper XML

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)

sql
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)

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

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> 等优化手段,性能可控

选择建议

  1. 首选 resultType

    • 当你进行单表查询,且数据库列名与 Java 类的属性名遵循统一规范时(例如,都使用下划线,并在 MyBatis 中开启驼峰转换)。
    • 当你的查询结果只是一个简单的值(如 count(*))或一个简单的对象时。
    • 原则:能用 resultType 解决的,就不要用 resultMap,保持简洁。
  2. 必须使用 resultMap

    • 当你的查询是多表连接查询,需要将结果映射到包含嵌套对象对象集合的复杂 Java 对象时(这是最常见的 resultMap 使用场景)。
    • 当数据库列名和 Java 属性名完全不一致,且你不想在 SQL 中使用大量 AS 别名来重命名列时。
    • 当你需要利用 MyBatis 的高级特性,如鉴别器 (discriminator)延迟加载 (Lazy Loading) 等。
    • 原则:当 resultType 无法满足你的映射需求时,resultMap 就是你的终极解决方案。

总之,resultTyperesultMap 是 MyBatis 为不同复杂度的场景提供的两种映射方案。理解它们的区别并根据实际情况做出正确选择,是高效使用 MyBatis 的关键。

00:00
00:00