ibatis 核心原理解析

最近查找一個生產問題的原因,需要深入研究 ibatis 框架的源碼。雖然最後證明問題的原因與 ibatis 無關,但是這個過程加深了對 ibatis 框架原理的理解。

這篇文章主要就來講講 ibatis 框架的原理。

可能現在很多人已不再使用 ibatis 或者說也沒聽 ibatis,不過肯定了解過 Mybatis。ibatis 就是 Mybatis框架的前身,雖然 ibatis 框架已經比較老,但是其核心功能與 Mybatis 一致。

ibatis 解決的痛點

我們先看一個使用 JDBC 查詢的例子。

QueryDaoByJDBC1.png

使用原生 JDBC 查詢,存在兩個痛點:

  1. 使用非常繁瑣,且需要處理各種數據庫異常,並且還需要關閉各種資源。
  2. 數據轉化麻煩。查詢之前需要從 Java 對象屬性值設置到 PreparedStatement中,查詢返回之後又需要從 ResultSet獲取返回設置到返回對象中。

在 ibatis 中封裝這些繁雜數據庫連接查詢代碼,並處理了各類異常以及關閉各種資源。另外 ibatis 自動處理 Java 對象與數據庫類型之間的自動轉化,讓業務代碼與 SQL 代碼之間做到了解耦。

數據類型轉化原理

數據類型轉化主要分爲兩類,一,傳入查詢的 Java 對象數據轉化成 SQL 類型數據。二 查詢返回的數據庫信息映射到 Java 對象中。

ibatis SQL 需要定義在配置文件中,一個查詢 SQL 語句配置如下:


      <select id="queryName" parameterClass="com.query.QueryDO"  resultClass="com.query.QueryDO" >
        select  * from  TEST_QUERY where ID=#id#

      </select>

ibatis 框架啓動過程將會解析配置文件,生成 MappedStatement 的子類。如 select 配置會生成對應的 SelectStatement 對象。

MappedStatement 相關類圖如下。

MappedStatement.png

MappedStatement 中將會保存存在兩個重要的對象,ParameterMapResultMap,通過這兩個對象將會完成 Java 類型與數據庫類型的相互轉化。

Java 對象轉化成數據庫類型

以上面 select 配置爲例,我們這裏需要做的是從傳入的 com.query.QueryDO對象中獲取屬性值,然後通過 PreparedStatement.setxx 設置到查詢參數中。

ibatis 解析配置中 SQL 語句時,將會獲取 # 之間的內容,將其替換成 ?。然後按照順序保存到一個 ParameterMapping[] 數組中,這個數組將會保存到 ParameterMap 對象中。

ParameterMapping 將會保存解析字段相關信息。

ParameterMapping.png

最終解析後的 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.png

TypeHandler 存在很多子類,通過這些子類正確處理了 Java 對象與數據庫類型轉化。

轉化的時序圖爲:

Java 對象與數據庫對象轉化

時序圖來源於:https://www.ibm.com/developer...

數據庫字段映射到 Java 對象

SQL 執行結束之後將會返回查詢結果,這裏將會使 SQL 查詢結果轉化爲返回結果 com.query.QueryDO。這裏需要用到上面提到 ResultMap 對象。

當 SQL 執行結束返回 ResultSet 對象之後,使用 ResultSet.getMetaData() 獲取返回信息元數據對象 ResultSetMetaData

ResultSetMetaData 可以獲取返回結果字段名,類型等信息,然後按照順序存入 ResultMapping 數組中。

然後按照 ResultMapping 數組中使用 TypeHandler調用 ResultSet.getxx 獲取實際返回數據,保存到 columnValues 數組中。

ResultMap 對象會根據 ResultMappingresultClass指定的類型合適的 dataExchange resultPlan對象。resultPlan對象與上面的 parameterPlan 對象一樣也會保存着變量的 setter 和 getter 方法數組。

最後先根據 resultClass 反射生成返回對象,然後使用反射調用 resultPlan setter 方法,依次設置相關值。

映射返回對象時序圖爲:

數據庫字段映射到 Java 對象

時序圖來源於:https://www.ibm.com/developer...

ibatis 樣板代碼

上面講完了 ibatis 數據類型的轉化原理,接着我們來看下 ibatis 調用 JDBC 樣板代碼。

使用 ibatis 執行查詢語句時,如 queryForObject,調用到 SqlMapExecutorDelegate 。在 SqlMapExecutorDelegate 中將會會做一些前提準備,比如準備事務,最後會將 SQL 語句委託給 SqlExecutor 執行。

image.png

這裏使用委託者模式,接受請求的對象將請求委託給另一個對象來處理。這種模式的優點在於解耦了業務代碼與實際執行代碼的聯繫,在於對外隱藏真正執行對象,易於擴展。

SqlExecutor#executeQuery 執行過程主要分爲以下三步。

image.png

第一步,獲取 PreparedStatement,使用 conn.prepareStatement(sql) 獲取。

image.png

第二步調用 PreparedStatement.setxxx 方法設置參數。上文中的 Java 對象類型轉化成 SQL 類型在這裏完成。

第三步,調用 PreparedStatement.execute() 執行 SQL 語句。

第四步,使用 ResultSet 獲取返回值,在這一步將會完成 數據庫類型與 Java 類型的轉化。

幫助鏈接

深入分析 iBATIS 框架之系統架構與映射原理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章