首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

MyBatis resultMap 核心映射機制詳解:從基礎到複雜關聯

     編輯:Java 相關知識

resultMap 存在意義

MyBatis 查詢映射支持 resultType(簡單自動映射)和 resultMap(精確控制)。resultMap 是最強大的映射特性,解決:

  • 字段名與屬性名不匹配(駝峰/下劃線)
  • 複雜關聯對象(一對一、一對多、多對多)
  • 嵌套結果集映射(JOIN 查詢)
  • 構造器注入、工廠方法等高級構建

基礎 resultMap 定義

<resultMap id="userResultMap" type="com.example.model.User">
  <id property="id" column="user_id" jdbcType="BIGINT"/>
  <result property="userName" column="user_name" jdbcType="VARCHAR"/>
  <result property="email" column="email" jdbcType="VARCHAR"/>
  <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>

<select id="selectUser" resultMap="userResultMap">
  SELECT user_id, user_name, email, create_time FROM t_user WHERE id = #{id}
</select>
  • id:標識主鍵,影響 equals/hashCode 與緩存鍵
  • result:普通字段映射
  • column:SQL 列名/別名
  • property:Java 屬性名
  • jdbcType:可選,針對 NULL 值處理優化

自動映射機制與 resultType 差異

使用 resultType 時,MyBatis 內部自動創建 ResultMap,規則:

  1. 列名/別名與屬性名完全匹配 → 直接賦值
  2. 開啟 mapUnderscoreToCamelCase=true → 下劃線自動轉駝峰
  3. 類型兼容 → 自動類型轉換

何時必須用 resultMap:列名屬性名差異大、關聯對象、構造器注入、一對多集合、分步查詢。

一對一關聯映射:<association>

場景:Blog 1:1 Author

public class Blog {
    private Long id;
    private String title;
    private Author author; // 關聯對象
}

public class Author {
    private Long id;
    private String name;
    private String email;
}

方式一:嵌套結果映射(JOIN 查詢,單次 SQL)

<resultMap id="blogDetailMap" type="Blog">
  <id property="id" column="blog_id"/>
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="name" column="author_name"/>
    <result property="email" column="author_email"/>
  </association>
</resultMap>

<select id="selectBlogWithAuthor" resultMap="blogDetailMap">
  SELECT b.id blog_id, b.title blog_title,
         a.id author_id, a.name author_name, a.email author_email
  FROM t_blog b LEFT JOIN t_author a ON b.author_id = a.id
  WHERE b.id = #{id}
</select>

方式二:分步查詢(N+1 問題風險,支持延遲加載)

<resultMap id="blogLazyMap" type="Blog">
  <id property="id" column="blog_id"/>
  <result property="title" column="blog_title"/>
  <association property="author" 
               select="selectAuthorById" 
               column="author_id" 
               fetchType="lazy"/> <!-- lazy/eager -->
</resultMap>

<select id="selectBlog" resultMap="blogLazyMap">
  SELECT id blog_id, title blog_title, author_id FROM t_blog WHERE id = #{id}
</select>

<select id="selectAuthorById" resultType="Author">
  SELECT id, name, email FROM t_author WHERE id = #{id}
</select>
  • select:關聯查詢語句 ID
  • column:主查詢列作為參數傳給關聯查詢
  • fetchType:覆蓋全局 lazyLoadingEnabled 設置

一對多集合映射:<collection>

場景:Blog 1:N Comment

public class Blog {
    private Long id;
    private String title;
    private List comments; // 集合屬性
}

public class Comment {
    private Long id;
    private String content;
    private Date createTime;
    private Blog blog; // 反向引用
}

嵌套結果映射(JOIN,注意去重)

<resultMap id="blogWithCommentsMap" type="Blog">
  <id property="id" column="blog_id"/> <!-- 關鍵:主鍵去重 -->
  <result property="title" column="blog_title"/>
  <collection property="comments" ofType="Comment">
    <id property="id" column="comment_id"/>
    <result property="content" column="comment_content"/>
    <result property="createTime" column="comment_time"/>
  </collection>
</resultMap>

<select id="selectBlogWithComments" resultMap="blogWithCommentsMap">
  SELECT b.id blog_id, b.title blog_title,
         c.id comment_id, c.content comment_content, c.create_time comment_time
  FROM t_blog b LEFT JOIN t_comment c ON b.id = c.blog_id
  WHERE b.id = #{id}
</select>
  • ofType:集合泛型類型(非 javaType)
  • 主鍵 <id> 必不可少:MyBatis 憑此去重,否則一條 Blog 會重複 N 次

分步查詢(避免 JOIN 爆炸,支持分頁)

<resultMap id="blogCommentsLazyMap" type="Blog">
  <id property="id" column="id"/>
  <result property="title" column="title"/>
  <collection property="comments" 
               select="selectCommentsByBlogId" 
               column="id" 
               fetchType="lazy"/>
</resultMap>

<select id="selectBlog" resultMap="blogCommentsLazyMap">
  SELECT id, title FROM t_blog WHERE id = #{id}
</select>

<select id="selectCommentsByBlogId" resultType="Comment">
  SELECT id, content, create_time FROM t_comment WHERE blog_id = #{id}
</select>

雙向關聯:打破循環引用

Comment 中引用 Blog,Blog 中引用 Comment 列表。MyBatis 通過 resultMap 互相引用自動處理:

<resultMap id="commentMap" type="Comment">
  <id property="id" column="comment_id"/>
  <result property="content" column="comment_content"/>
  <association property="blog" resultMap="blogSimpleMap"/> <!-- 引用簡化 Blog 映射 -->
</resultMap>

<resultMap id="blogSimpleMap" type="Blog">
  <id property="id" column="blog_id"/>
  <result property="title" column="blog_title"/>
  <!-- 不包含 comments,避免無限遞歸 -->
</resultMap>

鑒別器:多態映射

根據字段值動態選擇 resultMap(如不同類型用戶映射到不同子類):

<resultMap id="vehicleMap" type="Vehicle">
  <id property="id" column="id"/>
  <result property="brand" column="brand"/>
  <discriminator javaType="String" column="vehicle_type">
    <case value="CAR" resultMap="carMap"/>
    <case value="TRUCK" resultMap="truckMap"/>
  </discriminator>
</resultMap>

<resultMap id="carMap" type="Car" extends="vehicleMap">
  <result property="seatCount" column="seat_count"/>
</resultMap>

構造器注入:不可變對象支持

<resultMap id="userMap" type="User">
  <constructor>
    <idArg column="id" javaType="Long" name="id"/>
    <arg column="user_name" javaType="String" name="userName"/>
    <arg column="email" javaType="String" name="email"/>
  </constructor>
</resultMap>

-- 對應構造器
public User(Long id, String userName, String email) { ... }

最佳實踐總結

  1. 簡單查詢用 resultType + mapUnderscoreToCamelCase,減少樣板代碼
  2. 關聯對象優先嵌套結果映射(JOIN),單次查詢,避免 N+1
  3. 一對多必加 <id> 去重,否則主對象重複
  4. 大數據量/分頁場景用分步查詢,配合 fetchType="lazy" 按需加載
  5. 雙向關聯用簡化 resultMap 打破循環
  6. 不可變對象用構造器注入
  7. 複雜繼承體系用鑒別器

總結

resultMap 是 MyBatis 映射核心,掌握 <id/result> 基礎映射、<association> 一對一、<collection> 一對多、discriminator 多態、constructor 構造器注入、嵌套結果 vs 分步查詢權衡,能應對 99% 的 ORM 映射場景。原則:簡單自動、複雜精確、關聯去重、按需加載、避免循環。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved