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

MyBatis 高級映射完全指南:resultMap 實現一對一、一對多、多對多關聯查詢

     編輯:Java 綜合教學

前言:為什麼需要 resultMap?

MyBatis 默認的 resultType 只能將查詢結果平鋪映射到單個 POJO,無法處理對象圖(嵌套對象/集合)。resultMap 通過 <association>(一對一)和 <collection>(一對多)實現高級映射,支持:

  • 嵌套對象映射(如 Order → User)
  • 集合映射(如 Order → List<OrderDetail>)
  • 多級嵌套(如 User → List<Order> → List<OrderDetail> → Item)
  • 延遲加載(resultType 無法實現)

一、領域模型設計

// 用戶
public class User {
    private Integer id;
    private String username, sex, address;
    private List<Order> ordersList; // 一對多
    // getter/setter
}

// 訂單
public class Order {
    private Integer id, userId;
    private String number, note;
    private Date createtime;
    private User user;              // 多對一/一對一
    private List<OrderDetail> orderdetails; // 一對多
    // getter/setter
}

// 訂單明細
public class OrderDetail {
    private Integer id, ordersId, itemsId, itemsNum;
    private Item item;              // 多對一
    // getter/setter
}

// 商品
public class Item {
    private Integer id;
    private String name, detail;
    private Double price;
    // getter/setter
}

二、一對一查詢:訂單關聯用戶

方案 A:resultType + 扁平化 POJO(簡單場景)

// 1. 扁平化 POJO
public class OrderUserFlat extends Order {
    private String username, sex, address; // 直接扁平化用戶字段
}

// 2. Mapper XML
<select id="findOrderUserFlat" resultType="com.example.po.OrderUserFlat">
    SELECT o.*, u.username, u.sex, u.address
    FROM orders o, user u
    WHERE o.user_id = u.id
</select>

// 3. 適用場景:單純展示、無需對象圖、不需要延遲加載

方案 B:resultMap + association(標準對象圖,支持延遲加載)

<!-- 1. 定義 resultMap -->
<resultMap id="orderUserMap" type="com.example.po.Order">
    <id property="id" column="id"/>
    <result property="userId" column="user_id"/>
    <result property="number" column="number"/>
    <result property="createtime" column="createtime"/>
    <result property="note" column="note"/>
    
    <!-- association:一對一關聯單個對象 -->
    <association property="user" javaType="com.example.po.User">
        <id property="id" column="user_id"/> <!-- 關聯對象唯一標識 -->
        <result property="username" column="username"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
    </association>
</resultMap>

<select id="findOrderUser" resultMap="orderUserMap">
    SELECT o.*, u.username, u.sex, u.address
    FROM orders o, user u
    WHERE o.user_id = u.id
</select>

關鍵點解析

  • <id>:標識主鍵列,MyBatis 用於結果去重(合併同一訂單的多行結果)
  • javaType:指定關聯對象的具體類型
  • property="user":映射到 Order 類的 user 屬性

三、一對多查詢:訂單關聯明細

<!-- 繼承 orderUserMap 復用用戶映射 -->
<resultMap id="orderDetailMap" type="com.example.po.Order" extends="orderUserMap">
    <!-- collection:一對多關聯集合 -->
    <collection property="orderdetails" ofType="com.example.po.OrderDetail">
        <id property="id" column="orderdetail_id"/>
        <result property="itemsId" column="items_id"/>
        <result property="itemsNum" column="items_num"/>
        <result property="ordersId" column="orders_id"/>
    </collection>
</resultMap>

<select id="findOrderWithDetails" resultMap="orderDetailMap">
    SELECT o.*, u.username, u.sex, u.address,
           od.id orderdetail_id, od.items_id, od.items_num, od.orders_id
    FROM orders o, user u, orderdetail od
    WHERE o.user_id = u.id AND od.orders_id = o.id
</select>

collection 關鍵屬性

  • property:映射到主對象的集合屬性名(orderdetails)
  • ofType:集合泛型類型(非 javaType),對應 List<OrderDetail> 中的 OrderDetail
  • id:集合元素的唯一標識,用於去重

四、多對多查詢:用戶-訂單-明細-商品四級嵌套

<resultMap id="userOrderItemMap" type="com.example.po.User">
    <id property="id" column="user_id"/>
    <result property="username" column="username"/>
    <result property="sex" column="sex"/>
    <result property="address" column="address"/>
    
    <!-- 用戶 → 訂單列表 (一對多) -->
    <collection property="ordersList" ofType="com.example.po.Order">
        <id property="id" column="id"/>
        <result property="userId" column="user_id"/>
        <result property="number" column="number"/>
        <result property="createtime" column="createtime"/>
        <result property="note" column="note"/>
        
        <!-- 訂單 → 明細列表 (一對多) -->
        <collection property="orderdetails" ofType="com.example.po.OrderDetail">
            <id property="id" column="orderdetail_id"/>
            <result property="itemsId" column="items_id"/>
            <result property="itemsNum" column="items_num"/>
            <result property="ordersId" column="orders_id"/>
            
            <!-- 明細 → 商品 (一對一) -->
            <association property="item" javaType="com.example.po.Item">
                <id property="id" column="items_id"/>
                <result property="name" column="items_name"/>
                <result property="detail" column="items_detail"/>
                <result property="price" column="items_price"/>
            </association>
        </collection>
    </collection>
</resultMap>

<select id="findUserOrdersItems" resultMap="userOrderItemMap">
    SELECT u.*, o.*, od.id orderdetail_id, od.items_id, od.items_num, od.orders_id,
           i.id items_id, i.name items_name, i.detail items_detail, i.price items_price
    FROM user u, orders o, orderdetail od, item i
    WHERE u.id = o.user_id AND o.id = od.orders_id AND od.items_id = i.id
</select>

五、resultMap 進階特性

1. resultMap 繼承

<resultMap id="baseOrderMap" type="Order">...</resultMap>
<resultMap id="orderWithUserMap" extends="baseOrderMap">
    <association property="user" .../>
</resultMap>
<resultMap id="orderWithDetailsMap" extends="orderWithUserMap">
    <collection property="orderdetails" .../>
</resultMap>

2. 自動映射

<resultMap id="autoMap" type="Order" autoMapping="true">
    <id property="id" column="id"/>
    <!-- 其他屬性自動按列名匹配屬性名 -->
</resultMap>

3. 延遲加載配置

<!-- 全局開啟 (mybatis-config.xml) -->
<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

<!-- 局部配置 -->
<association property="user" javaType="User" select="selectUserById" column="user_id" fetchType="lazy"/>
<collection property="orderdetails" ofType="OrderDetail" select="selectDetailsByOrderId" column="id" fetchType="lazy"/>

六、resultType vs resultMap 選擇指南

維度resultTyperesultMap
映射能力平鋪映射單個 POJO對象圖、嵌套集合、繼承、自動映射
配置複雜度零配置(約定大於配置)需顯式定義 XML
延遲加載不支持支持(核心優勢)
去重機制無(依賴 POJO 設計)基於 <id> 標識自動合併
適用場景簡單查詢、報表展示、扁平化 DTO領域模型驅動、複雜關聯、需要延遲加載
性能稍快(無解析開銷)略慢(解析映射),但避免 N+1 查詢

七、最佳實踐建議

  1. 優先使用 resultType:簡單業務、扁平化 DTO、報表導出
  2. 領域模型驅動設計:先設計對象圖(聚合根、實體、值對象),再設計 resultMap
  3. 合理使用延遲加載:列表頁面不需要明細時延遲加載,詳情頁面急用時可考慮急加載(JOIN)或預加載
  4. 避免過度嵌套:超過 3 級嵌套考慮拆分查詢或用 DTO 扁平化
  5. 列名別名規範:SQL 中使用 AS 別名與屬性名一致,減少映射配置
  6. 主鍵 <id> 必須正確:錯誤的 id 導致結果合併錯誤(數據重複或丟失)

八、測試驗證代碼

@Test
public void testUserOrdersItems() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
    List<User> users = mapper.findUserOrdersItems();
    
    // 驗證四級導航
    User user = users.get(0);
    Order order = user.getOrdersList().get(0);
    OrderDetail detail = order.getOrderdetails().get(0);
    Item item = detail.getItem();
    
    System.out.println("用戶: " + user.getUsername());
    System.out.println("訂單: " + order.getNumber());
    System.out.println("明細商品: " + item.getName() + " - " + item.getPrice());
    
    sqlSession.close();
}

總結:resultMap 是 MyBatis 區別於其他 ORM 的核心特性,通過 <association>(一對一)和 <collection>(一對多)構建完整對象圖。掌握 <id> 去重原理、ofType vs javaType、繼承復用、延遲加載配置,即可駕馭 99% 複雜映射場景。記住:SQL 寫好 JOIN,映射配對準,對象圖自然成。

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