1. XMLMapperBuilder
如何將 SQL 語句解析成可執行對象 (MappedStatement):
XMLMapperBuilder
解析 <select>
, <insert>
, <update>
, <delete>
等 SQL 語句元素時,并不僅僅是簡單地讀取 SQL 文本,而是要將 SQL 語句和相關的配置信息 封裝成 MappedStatement
對象,MappedStatement
對象才是 MyBatis 運行時真正可執行的 SQL 對象。 這個過程主要涉及以下幾個關鍵步驟:
-
1.1. 解析 SQL 語句文本 (SqlSource):
XMLMapperBuilder
會讀取<select>
,<insert>
,<update>
,<delete>
元素體內的 SQL 語句文本。 SQL 語句文本可能包含:- 靜態 SQL: 普通的 SQL 語句,不包含任何動態 SQL 標簽或占位符。
- 動態 SQL: 包含 MyBatis 的動態 SQL 標簽 (例如
<if>
,<choose>
,<foreach>
,<where>
,<set>
,<trim>
,${}
,#{}
等)。 - 參數占位符:
#{}
和${}
占位符,用于在運行時動態地替換參數值。
XMLMapperBuilder
會根據 SQL 語句文本的類型,創建不同的SqlSource
對象來表示 SQL 語句的來源和處理方式。SqlSource
接口是 MyBatis 中表示 SQL 語句來源的抽象接口,它有以下幾種實現類:RawSqlSource
: 用于表示 靜態 SQL。 對于靜態 SQL,SQL 語句文本在解析時就已經確定,運行時無需動態構建。DynamicSqlSource
: 用于表示 動態 SQL。 對于動態 SQL,SQL 語句文本在運行時需要根據參數值進行動態構建。DynamicSqlSource
會負責解析和處理動態 SQL 標簽,并生成最終的可執行 SQL 語句。ProviderSqlSource
: 用于表示 基于 Provider 類的 SQL。 SQL 語句不是直接寫在 XML 文件中,而是通過一個 Java Provider 類動態生成。
XMLMapperBuilder
會根據 SQL 語句是否包含動態 SQL 標簽來判斷創建RawSqlSource
還是DynamicSqlSource
。 如果 SQL 語句中使用了動態 SQL 標簽,則創建DynamicSqlSource
,否則創建RawSqlSource
。 如果是基于 Provider 類的 SQL,則創建ProviderSqlSource
。示例代碼 (簡化版,展示
XMLMapperBuilder
如何創建SqlSource
):// 假設 XMLMapperBuilder 解析 <select id="getUserById"> 元素 XNode selectNode = ...; // 代表 <select> 元素的 XNode 對象 String sqlText = selectNode.getStringBody(); // 獲取 <select> 元素體內的 SQL 語句文本SqlSource sqlSource; if (sqlTextContainsDynamicSqlTags(sqlText)) { // 檢查 SQL 文本是否包含動態 SQL 標簽 (簡化判斷邏輯)sqlSource = new DynamicSqlSource(configuration, selectNode); // 創建 DynamicSqlSource } else {sqlSource = new RawSqlSource(configuration, selectText); // 創建 RawSqlSource }
-
1.2. 解析參數映射 (ParameterMap 和 內聯參數映射):
XMLMapperBuilder
會解析 SQL 語句中的參數映射配置。 MyBatis 支持兩種參數映射方式:parameterMap
屬性 (已過時,不推薦使用): 通過<select>
,<insert>
,<update>
,<delete>
元素的parameterMap
屬性引用外部定義的<parameterMap>
元素。XMLMapperBuilder
會解析<parameterMap>
元素及其子元素<parameter>
,構建ParameterMap
對象,并將其關聯到MappedStatement
。parameterMap
方式已經過時,不推薦使用。- 內聯參數映射 (
#{}
和${}
): 在 SQL 語句文本中直接使用#{}
和${}
占位符進行參數映射。 這是 現代 MyBatis 開發中推薦使用的參數映射方式。XMLMapperBuilder
會解析 SQL 語句文本中的#{}
和${}
占位符,并提取占位符中的參數屬性名、jdbcType、typeHandler 等信息。
XMLMapperBuilder
會將解析得到的參數映射信息 存儲到MappedStatement
對象中,以便在運行時進行參數綁定。 -
1.3. 解析結果映射 (ResultMap 和 resultType):
XMLMapperBuilder
會解析<select>
元素的結果映射配置,用于將查詢結果集映射到 Java 對象。 MyBatis 支持兩種結果映射方式:resultMap
屬性: 通過<select>
元素的resultMap
屬性引用外部定義的<resultMap>
元素。XMLMapperBuilder
會解析<resultMap>
元素及其子元素 (<id>
,<result>
,<association>
,<collection>
,<discriminator>
), 構建ResultMap
對象,并將其關聯到MappedStatement
。resultMap
方式適用于復雜的結果集映射場景 (例如關聯查詢、集合屬性、多態映射等)。resultType
屬性: 通過<select>
元素的resultType
屬性直接指定結果類型。resultType
方式適用于簡單的結果集映射場景 (例如單表查詢,結果類型是基本類型或 POJO)。 MyBatis 會自動進行簡單的屬性映射。
XMLMapperBuilder
會根據<select>
元素配置的resultMap
或resultType
屬性, 創建ResultMap
對象 (如果使用resultMap
) 或記錄resultType
,并將結果映射信息存儲到MappedStatement
對象中,以便在運行時進行結果集映射。 -
1.4. 構建
MappedStatement
對象:XMLMapperBuilder
在完成 SQL 語句文本 (SqlSource
)、參數映射和結果映射的解析后,會將這些信息,以及<select>
,<insert>
,<update>
,<delete>
元素上的其他屬性 (例如statementType
,timeout
,fetchSize
,cache
等), 整合到一個MappedStatement
對象中。MappedStatement
對象包含了執行一個 SQL 操作所需的所有信息,是 MyBatis 運行時執行 SQL 的核心對象。 -
1.5. 注冊
MappedStatement
到Configuration
:XMLMapperBuilder
會將構建好的MappedStatement
對象 注冊到Configuration
對象的mappedStatements
屬性 (一個StrictMap<MappedStatement>
) 中。 注冊時,會使用 Mapper 接口的全限定名 + SQL 語句的id
屬性作為MappedStatement
的唯一 ID (mappedStatementId)。
2. XMLMapperBuilder
如何將結果映射規則解析成對應的處理器 (ResultMap 和 ResultHandler):
XMLMapperBuilder
在解析 <resultMap>
元素時,會構建 ResultMap
對象。 ResultMap
對象本身 不是處理器 (Handler),而是 結果映射規則的定義。 它描述了如何將查詢結果集中的列映射到 Java 對象的屬性。
真正的結果集映射處理器是 ResultHandler
接口的實現類。 ResultHandler
接口負責 逐行處理查詢結果集,并將每一行數據按照 ResultMap
或 resultType
定義的映射規則,映射到 Java 對象。
XMLMapperBuilder
在解析結果映射規則時,主要完成以下工作,為后續的結果集映射處理做準備:
-
2.1. 構建
ResultMap
對象 (如果定義了<resultMap>
):XMLMapperBuilder
解析<resultMap>
元素時,會根據<resultMap>
元素的配置信息 (包括id
,type
,extends
,autoMapping
,<constructor>
,<id>
,<result>
,<association>
,<collection>
,<discriminator>
), 構建一個ResultMap
對象。ResultMap
對象會存儲結果映射的所有規則,例如:id
:resultMap
的唯一 ID。type
: 結果映射的目標 Java 類型。resultMappings
: 一個List<ResultMapping>
, 存儲了所有的屬性映射規則 (<id>
,<result>
,<association>
,<collection>
,<discriminator>
對應的ResultMapping
對象)。constructorResultMappings
,idResultMappings
,propertyResultMappings
,associationResultMappings
,collectionResultMappings
,discriminatorResultMappings
: 不同類型的ResultMapping
列表,方便按類型查找。autoMapping
: 是否開啟自動映射。extendsResultMap
: 繼承的ResultMap
的 ID。discriminator
: 鑒別器 (Discriminator)。
ResultMap
對象本身并不執行映射操作,它只是結果映射規則的描述。 -
2.2. 將
ResultMap
對象注冊到Configuration
:XMLMapperBuilder
會將構建好的ResultMap
對象 注冊到Configuration
對象的resultMapRegistry
屬性 (一個ResultMapRegistry
對象) 中,使用resultMap
的id
作為 key。 -
2.3. 運行時,MyBatis 使用
ResultHandler
和ResultMap
進行結果集映射:在 MyBatis 運行時執行
SqlSession.selectList()
,SqlSession.selectOne()
等查詢方法時,MyBatis 會:- 獲取
MappedStatement
對象: 根據 Mapper 接口方法和方法名 (或 SQL 語句 ID) 找到對應的MappedStatement
對象。 - 執行 SQL 查詢: 使用 JDBC 執行
MappedStatement
中定義的 SQL 語句,獲取ResultSet
(結果集)。 - 創建
ResultHandler
實例 (或使用默認的DefaultResultHandler
):ResultHandler
負責處理結果集。 MyBatis 通常使用默認的DefaultResultHandler
,也可以自定義ResultHandler
。 - 獲取
ResultMap
對象 (或resultType
): 從MappedStatement
對象中獲取ResultMap
對象 (如果配置了resultMap
) 或resultType
(如果配置了resultType
)。 - 逐行處理
ResultSet
:ResultHandler
會逐行遍歷ResultSet
,對于每一行數據:- 根據
ResultMap
(或resultType
) 定義的映射規則,將ResultSet
當前行的數據映射到 Java 對象。 這個映射過程會涉及到類型轉換、屬性賦值、關聯對象/集合的創建和賦值等復雜操作。 - 將映射后的 Java 對象添加到結果列表 (如果是
selectList
) 或直接返回 (如果是selectOne
)。
- 根據
ResultHandler
和ResultMap
協同工作,完成了結果集到 Java 對象的映射過程。ResultMap
定義映射規則,ResultHandler
負責執行映射操作。 - 獲取
總結:
XMLMapperBuilder
解析 SQL 語句時,會將 SQL 語句文本、參數映射、結果映射等信息封裝成MappedStatement
對象,MappedStatement
是 MyBatis 運行時可執行的 SQL 對象。XMLMapperBuilder
解析<resultMap>
元素時,會構建ResultMap
對象,ResultMap
對象定義了結果集映射規則,但本身不執行映射操作。ResultHandler
接口及其實現類 才是真正負責結果集映射的處理器。 MyBatis 運行時使用ResultHandler
和ResultMap
協同工作,將查詢結果集逐行映射到 Java 對象。XMLMapperBuilder
的解析工作為 MyBatis 運行時執行 SQL 和進行結果集映射提供了必要的配置信息和對象模型。