最近查找一個生產問題的緣由,須要深刻研究 ibatis 框架的源碼。雖然最後證實問題的緣由與 ibatis 無關,可是這個過程加深了對 ibatis 框架原理的理解。html
這篇文章主要就來說講 ibatis 框架的原理。java
可能如今不少人已再也不使用 ibatis 或者說也沒聽 ibatis,不過確定瞭解過 Mybatis。ibatis 就是 Mybatis框架的前身,雖然 ibatis 框架已經比較老,可是其核心功能與 Mybatis 一致。
咱們先看一個使用 JDBC 查詢的例子。sql
使用原生 JDBC 查詢,存在兩個痛點:數據庫
PreparedStatement
中,查詢返回以後又須要從 ResultSet
獲取返回設置到返回對象中。在 ibatis 中封裝這些繁雜數據庫鏈接查詢代碼,並處理了各種異常以及關閉各類資源。另外 ibatis 自動處理 Java 對象與數據庫類型之間的自動轉化,讓業務代碼與 SQL 代碼之間作到了解耦。數組
數據類型轉化主要分爲兩類,一,傳入查詢的 Java 對象數據轉化成 SQL 類型數據。二 查詢返回的數據庫信息映射到 Java 對象中。架構
ibatis SQL 須要定義在配置文件中,一個查詢 SQL 語句配置以下:app
<select id="queryName" parameterClass="com.query.QueryDO" resultClass="com.query.QueryDO" > select * from TEST_QUERY where ID=#id# </select>
ibatis 框架啓動過程將會解析配置文件,生成 MappedStatement
的子類。如 select 配置會生成對應的 SelectStatement
對象。框架
MappedStatement
相關類圖以下。spa
在 MappedStatement
中將會保存存在兩個重要的對象,ParameterMap
與 ResultMap
,經過這兩個對象將會完成 Java 類型與數據庫類型的相互轉化。code
以上面 select 配置爲例,咱們這裏須要作的是從傳入的 com.query.QueryDO
對象中獲取屬性值,而後經過 PreparedStatement.setxx
設置到查詢參數中。
ibatis 解析配置中 SQL 語句時,將會獲取 # 之間的內容,將其替換成 ?
。而後按照順序保存到一個 ParameterMapping[]
數組中,這個數組將會保存到 ParameterMap
對象中。
ParameterMapping 將會保存解析字段相關信息。
最終解析後的 SQL 爲:
select * from TEST_QUERY where ID=?
該 SQL 就能夠經過 connection.prepareStatement("select * from TEST_QUERY where ID=?");
生成 PreparedStatement
對象。
接着 ibatis 會根據 ParameterMapping
和 parameterClass
指定的類型建立合適的 dataExchange
和 parameterPlan
對象。
其中 parameterPlan
對象會按照 ParameterMapping
數組中順序保存了變量的 setter 和 getter 方法數組。
dataExchange
會按照 ParameterMapping
數組中的順序使用反射獲取 parameterPlan getter 方法返回值生成 parameters
數組。
最後循環 ParameterMapping 數組,在 TypeHandler
調用 PreparedStatement.setxx
設置相關值。
TypeHandler
存在不少子類,經過這些子類正確處理了 Java 對象與數據庫類型轉化。
轉化的時序圖爲:
時序圖來源於: https://www.ibm.com/developer...
SQL 執行結束以後將會返回查詢結果,這裏將會使 SQL 查詢結果轉化爲返回結果 com.query.QueryDO
。這裏須要用到上面提到 ResultMap
對象。
當 SQL 執行結束返回 ResultSet
對象以後,使用 ResultSet.getMetaData()
獲取返回信息元數據對象 ResultSetMetaData
。
從 ResultSetMetaData
能夠獲取返回結果字段名,類型等信息,而後按照順序存入 ResultMapping
數組中。
而後按照 ResultMapping
數組中使用 TypeHandler
調用 ResultSet.getxx
獲取實際返回數據,保存到 columnValues
數組中。
在 ResultMap
對象會根據 ResultMapping
與 resultClass
指定的類型合適的 dataExchange
和 resultPlan
對象。resultPlan
對象與上面的 parameterPlan
對象同樣也會保存着變量的 setter 和 getter 方法數組。
最後先根據 resultClass
反射生成返回對象,而後使用反射調用 resultPlan
setter 方法,依次設置相關值。
映射返回對象時序圖爲:
時序圖來源於: https://www.ibm.com/developer...
上面講完了 ibatis 數據類型的轉化原理,接着咱們來看下 ibatis 調用 JDBC 樣板代碼。
使用 ibatis 執行查詢語句時,如 queryForObject
,調用到 SqlMapExecutorDelegate
。在 SqlMapExecutorDelegate
中將會會作一些前提準備,好比準備事務,最後會將 SQL 語句委託給 SqlExecutor
執行。
這裏使用委託者模式,接受請求的對象將請求委託給另外一個對象來處理。這種模式的優勢在於解耦了業務代碼與實際執行代碼的聯繫,在於對外隱藏真正執行對象,易於擴展。
在 SqlExecutor#executeQuery
執行過程主要分爲如下三步。
第一步,獲取 PreparedStatement
,使用 conn.prepareStatement(sql)
獲取。
第二步調用 PreparedStatement.setxxx
方法設置參數。上文中的 Java 對象類型轉化成 SQL 類型在這裏完成。
第三步,調用 PreparedStatement.execute()
執行 SQL 語句。
第四步,使用 ResultSet
獲取返回值,在這一步將會完成 數據庫類型與 Java 類型的轉化。