MyBatis 默認的 resultType 只能將查詢結果平鋪映射到單個 POJO,無法處理對象圖(嵌套對象/集合)。resultMap 通過 <association>(一對一)和 <collection>(一對多)實現高級映射,支持:
// 用戶
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
}
// 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. 適用場景:單純展示、無需對象圖、不需要延遲加載
<!-- 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>
property:映射到主對象的集合屬性名(orderdetails)ofType:集合泛型類型(非 javaType),對應 List<OrderDetail> 中的 OrderDetailid:集合元素的唯一標識,用於去重<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 id="baseOrderMap" type="Order">...</resultMap>
<resultMap id="orderWithUserMap" extends="baseOrderMap">
<association property="user" .../>
</resultMap>
<resultMap id="orderWithDetailsMap" extends="orderWithUserMap">
<collection property="orderdetails" .../>
</resultMap>
<resultMap id="autoMap" type="Order" autoMapping="true">
<id property="id" column="id"/>
<!-- 其他屬性自動按列名匹配屬性名 -->
</resultMap>
<!-- 全局開啟 (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 | resultMap |
|---|---|---|
| 映射能力 | 平鋪映射單個 POJO | 對象圖、嵌套集合、繼承、自動映射 |
| 配置複雜度 | 零配置(約定大於配置) | 需顯式定義 XML |
| 延遲加載 | 不支持 | 支持(核心優勢) |
| 去重機制 | 無(依賴 POJO 設計) | 基於 <id> 標識自動合併 |
| 適用場景 | 簡單查詢、報表展示、扁平化 DTO | 領域模型驅動、複雜關聯、需要延遲加載 |
| 性能 | 稍快(無解析開銷) | 略慢(解析映射),但避免 N+1 查詢 |
AS 別名與屬性名一致,減少映射配置<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>去重原理、ofTypevsjavaType、繼承復用、延遲加載配置,即可駕馭 99% 複雜映射場景。記住:SQL 寫好 JOIN,映射配對準,對象圖自然成。