MyBatis 的 <association> 標籤是實現一對一關聯映射的關鍵,支援巢狀結果映射與巢狀查詢兩種載入策略。本文透過「使用者-訂單」經典場景,完整示範從實體類設計、Mapper XML 配置、介面定義到測試驗證的全流程。
// 用戶實體
public class User {
private Long id;
private String username;
private Order order; // 一對一關聯
// getter/setter
}
// 訂單實體
public class Order {
private Long id;
private String orderNo;
private Double amount;
// getter/setter
}
CREATE TABLE t_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50)
);
CREATE TABLE t_order (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT,
order_no VARCHAR(32),
amount DECIMAL(10,2),
FOREIGN KEY (user_id) REFERENCES t_user(id)
);
<resultMap id="userOrderMap" type="com.example.User">
<id property="id" column="user_id"/>
<result property="username" column="username"/>
<association property="order" javaType="com.example.Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="amount" column="amount"/>
</association>
</resultMap>
<select id="selectUserWithOrder" resultMap="userOrderMap">
SELECT u.id AS user_id, u.username,
o.id AS order_id, o.order_no, o.amount
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id
WHERE u.id = #{userId}
</select>
優點: 單次 SQL、無 N+1 問題、效能最佳。
缺點:結果集列名需別名避免衝突;關聯對象為 null 時仍建立空物件(可配合 notNullColumn 優化)。
<resultMap id="userMap" type="com.example.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<association property="order"
column="id"
select="selectOrderByUserId"
fetchType="lazy"/>
</resultMap>
<select id="selectUser" resultMap="userMap">
SELECT id, username FROM t_user WHERE id = #{id}
</select>
<select id="selectOrderByUserId" resultType="com.example.Order">
SELECT id, order_no, amount FROM t_order WHERE user_id = #{userId}
</select>
關鍵配置: 全域啟用延遲載入 <setting name="lazyLoadingEnabled" value="true"/>。
優點: 按需查詢、避免載入巨大關聯物件。
缺點: 可能產生 N+1 查詢,需權衡。
public interface UserMapper {
User selectUserWithOrder(Long userId); // 策略一
User selectUser(Long id); // 策略二
}
| 場景 | 建議策略 | 關鍵配置 |
|---|---|---|
| 關聯表較小、頻繁同時存取 | 巢狀結果 (JOIN) | resultMap + association + 列別名 |
| 關聯表大、低頻存取 | 巢狀查詢 (延遲載入) | fetchType="lazy" + column 傳參 |
| 關聯可能為空 | 兩者皆可 | association notNullColumn="order_id" |
| 多層巢狀 (User→Order→Address) | 巢狀結果 | 多層 association 巢狀 |
AS) 區分 user.id 與 order.idresultMap 的 <id> 必須唯一標識根物件,關聯物件也需 <id>lazyLoadingEnabled、aggressiveLazyLoading 設定,避免 toString/序列化觸發誤載入javaType 必須為完整限定類名或別名註冊掌握 <association> 兩大核心策略與配置細節,即可靈活應對 MyBatis 一對一關聯映射的各種業務場景,在效能與維護性之間找到最佳平衡點。