MyBatis中Collection和Association的底層實現(xiàn)原理分析
引言
在 MyBatis 中,<collection> 和 <association> 標簽用于處理一對多和一對一的關(guān)系。
這兩個標簽在底層通過緩存、對象創(chuàng)建和反射機制,將數(shù)據(jù)庫結(jié)果集高效地映射為 Java 對象。
本文將重點探討這兩個標簽的底層實現(xiàn)原理,并結(jié)合實際的數(shù)據(jù)庫示例說明其工作機制。
1. Collection 的底層映射原理
1.1 一對多映射場景
<collection> 標簽主要用于表示一對多關(guān)系的映射。
典型的一對多場景是在一個表中有重復的主對象記錄(如用戶),但每個主對象包含多個子對象(如訂單)。
示例數(shù)據(jù)庫結(jié)構(gòu)
假設(shè)我們有以下結(jié)果集,表示用戶和訂單的關(guān)系:
| user_id | username | order_id | order_date |
|---|---|---|---|
| 1 | Alice | 101 | 2023-01-01 |
| 1 | Alice | 102 | 2023-02-01 |
| 2 | Bob | 103 | 2023-03-01 |
這個數(shù)據(jù)表示,用戶 Alice 有兩個訂單,用戶 Bob 有一個訂單。我們希望將其映射為一個 User 對象,每個用戶對象包含一個訂單集合。
MyBatis 映射配置
<resultMap id="userResultMap" type="com.example.User">
<id property="userId" column="user_id"/>
<result property="username" column="username"/>
<collection property="orders" ofType="com.example.Order">
<id property="orderId" column="order_id"/>
<result property="orderDate" column="order_date"/>
</collection>
</resultMap>
在這個映射中,<collection> 標簽表示用戶對象 User 可能包含多個 Order 對象,ofType 定義了集合中元素的類型。
1.2 底層映射機制
結(jié)果集處理
當執(zhí)行 SQL 查詢時,MyBatis 會獲取完整的結(jié)果集。由于用戶可能有多個訂單,查詢結(jié)果會包含重復的 user_id 和 username。
主對象緩存處理
MyBatis 使用主鍵 user_id 來判斷是否已經(jīng)創(chuàng)建了對應的 User 對象(如果沒有主鍵, 會使用其他鍵來模擬唯一鍵)。
- 如果
user_id相同,表示當前行屬于同一個用戶,則重用該用戶對象。 - 如果
user_id不同,MyBatis 會創(chuàng)建新的User對象。
集合屬性映射
對于每一行的訂單信息(order_id 和 order_date),MyBatis 會為當前的用戶創(chuàng)建新的 Order 對象,并將其添加到用戶的 orders 集合中。
對象重用與集合管理
通過緩存機制,MyBatis 確保每個用戶只創(chuàng)建一次,而訂單則根據(jù)不同的 order_id 創(chuàng)建。對于相同的用戶,每個訂單行都會被添加到 orders 集合中。
底層邏輯代碼示例(偽代碼)
// 查詢用戶緩存處理
if (!cache.containsKey(userId)) {
User user = new User();
user.setUserId(resultSet.getInt("user_id"));
user.setUsername(resultSet.getString("username"));
cache.put(userId, user);
}
// 處理訂單集合
Order order = new Order();
order.setOrderId(resultSet.getInt("order_id"));
order.setOrderDate(resultSet.getDate("order_date"));
// 將訂單添加到用戶的集合
user.getOrders().add(order);
MyBatis 會緩存用戶對象,并通過 getOrders().add(order) 方法將每個新創(chuàng)建的訂單對象加入到用戶的訂單集合中。
2. Association 的底層映射原理
2.1 一對一映射場景
<association> 標簽用于處理一對一或多對一的映射。在這種場景下,通常我們希望將兩個表的結(jié)果組合為一個主對象和一個關(guān)聯(lián)對象。例如,用戶和用戶詳情是一對一的關(guān)系。
示例數(shù)據(jù)庫結(jié)構(gòu)
假設(shè)我們有以下結(jié)果集,表示用戶及其詳細信息:
| user_id | username | detail_id | age | address |
|---|---|---|---|---|
| 1 | Alice | 1 | 25 | Wonderland |
| 2 | Bob | 2 | 30 | Wonderland |
這個數(shù)據(jù)表示,每個用戶有一條與之關(guān)聯(lián)的詳細信息記錄。我們希望將其映射為 User 對象,其中包含一個 UserDetail 對象。
MyBatis 映射配置
<resultMap id="userWithDetailResultMap" type="com.example.User">
<id property="userId" column="user_id"/>
<result property="username" column="username"/>
<association property="detail" javaType="com.example.UserDetail">
<id property="detailId" column="detail_id"/>
<result property="age" column="age"/>
<result property="address" column="address"/>
</association>
</resultMap>
在這個映射中,<association> 標簽用于將用戶與詳細信息關(guān)聯(lián)起來,javaType 指定了 UserDetail 對象的類型。
2.2 底層映射機制
主對象創(chuàng)建與緩存
當執(zhí)行查詢時,MyBatis 會首先根據(jù) user_id 創(chuàng)建或查找緩存中的用戶對象。類似于 <collection>,如果用戶對象已經(jīng)存在,則不會重新創(chuàng)建。
關(guān)聯(lián)對象的映射
對于每一行,MyBatis 根據(jù) detail_id 創(chuàng)建 UserDetail 對象,并通過 MetaObject 機制,將查詢結(jié)果的 age 和 address 列映射到 UserDetail 對象中。
關(guān)聯(lián)對象賦值
一旦 UserDetail 對象創(chuàng)建完成,MyBatis 會將其賦值給 User 對象的 detail 屬性。這是通過反射完成的。
底層邏輯代碼示例(偽代碼)
// 用戶緩存處理
User user = cache.get(userId);
// 創(chuàng)建并映射用戶詳細信息
UserDetail detail = new UserDetail();
detail.setDetailId(resultSet.getInt("detail_id"));
detail.setAge(resultSet.getInt("age"));
detail.setAddress(resultSet.getString("address"));
// 將詳細信息賦值給用戶對象
user.setDetail(detail);
MyBatis 在映射時會通過 MetaObject 訪問 User 對象的 detail 屬性,并使用反射為其賦值。這個過程使得每個用戶對象都能夠正確關(guān)聯(lián)其詳細信息。
3. 總結(jié)
MyBatis 中的 <collection> 和 <association> 標簽通過緩存、反射與對象創(chuàng)建機制,完成數(shù)據(jù)庫結(jié)果集到 Java 對象的映射。在處理一對多和一對一關(guān)系時,這兩個標簽的底層機制可以高效地組織復雜的對象結(jié)構(gòu)。
<collection>標簽:通過主對象的緩存和子對象集合的動態(tài)添加,實現(xiàn)了復雜的一對多關(guān)系的映射。<association>標簽:通過關(guān)聯(lián)對象的創(chuàng)建和屬性映射,實現(xiàn)了一對一或多對一的關(guān)聯(lián)映射。
理解這些底層機制能夠幫助開發(fā)者優(yōu)化查詢性能,并更好地設(shè)計數(shù)據(jù)結(jié)構(gòu),充分發(fā)揮 MyBatis 的 ORM 功能。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- MyBatis中高級多表查詢(ResultMap、association、collection)詳解
- MyBatis使用嵌套查詢collection和association的實現(xiàn)
- mybatis中association和collection的使用與區(qū)別
- MyBatis的collection和association的使用解讀
- Mybatis中一對多(collection)和一對一(association)的組合查詢使用
- MyBatis中的ResultMap的association和collection標簽詳解
- mybatis?collection和association的區(qū)別解析
相關(guān)文章
關(guān)于scanner.nextInt()等next()和scanner.nextIine()連用注意事項
這篇文章主要介紹了關(guān)于scanner.nextInt()等next()和scanner.nextIine()連用注意事項,具有很好的參考價值,希望對大家有所幫助。2023-04-04
解決SpringBoot運行報錯:找不到或無法加載主類的問題
這篇文章主要介紹了解決SpringBoot運行報錯:找不到或無法加載主類的問題,具有很好的參考價值,對大家的學習或工作有一定的參考價值,需要的朋友可以參考下2023-09-09
java關(guān)于String.split("|")的使用方式
這篇文章主要介紹了java關(guān)于String.split("|")的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
springcloud gateway聚合swagger2的方法示例
這篇文章主要介紹了springcloud gateway聚合swagger2的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-04-04
一文詳解SpringBoot如何創(chuàng)建自定義的自動配置
在實際開發(fā)中,僅靠SpringBoot的自動配置是遠遠不夠的,所以這篇文章主要來和大家簡單講講如何在SpringBoot創(chuàng)建自定義的自動配置吧2025-07-07

