通用源碼閱讀指導mybatis源碼詳解:type包

type包

type包中的類有 55個之多。在遇到這種繁雜的情況時,一定要注意歸類總結。

歸類總結是源碼閱讀中非常好的辦法。往往越是大量的類,越是大量的方法,越有規律進行分類。這些原本繁雜的類和方法經過分類後,可能會變得很有條理。

經過梳理後,我們把 type包內的類分為以下六組。

· 類型處理器:1個接口、1個基礎實現類、1個輔助類、43個實現類。

-TypeHandler:類型處理器接口;

-BaseTypeHandler:類型處理器的基礎實現;

-TypeReference:類型參考器;

-*TypeHandler:43個類型處理器。

· 類型註冊表:3個。

-SimpleTypeRegistry:基本類型註冊表,內部使用 Set 維護了所有 Java 基本數據類型的集合;

-TypeAliasRegistry:類型別名註冊表,內部使用 HashMap維護了所有類型的別名和類型的映射關係;

-TypeHandlerRegistry:類型處理器註冊表,內部維護了所有類型與對應類型處理器的映射關係。

· 註解類:3個。

-Alias:使用該註解可以給類設置別名,設置後,別名和類型的映射關係便存入TypeAliasRegistry中;

-MappedJdbcTypes:有時我們想使用自己的處理器來處理某些 JDBC 類型,只需創建 BaseTypeHandler 的子類,然後在上面加上該註解,聲明它要處理的JDBC類型即可;

-MappedTypes:有時我們想使用自己的處理器來處理某些Java類型,只需創建BaseTypeHandler的子類,然後在上面加上該註解,聲明它要處理的 Java類型即可。

· 異常類:1個。

-TypeException:表示與類型處理相關的異常。

· 工具類:1個。

-ByteArrayUtils:提供數組轉化的工具方法。

· 枚舉類:1個。

-JdbcType:在 Enum中定義了所有的 JDBC類型,類型來源於 java.sql.Types。

以上類中,註解類、異常類、工具類、枚舉類都非常簡單,不再單獨介紹。下面將重點介紹類型處理器和類型註冊表。

模板模式

說起模板大家應該都很熟悉。一般情況下,模板中規定了大體的框架,只留下一些細節供使用者來修改和完善。使用同一模板做出的不同產品都具有一致的框架。設計模式中的模板模式與上述模板的概念相同。在模板模式中,需要使用一個抽象類定義一套操作的整體步驟(即模板),而抽象類的子類則完成每個步驟的具體實現。這樣,抽象類的不同子類遵循了同樣的一套模板。

例如,我們定義一套打掃衛生的模板,如代碼8-1 所示。它為所有的打掃衛生工作定義了四個大的步驟:準備(prepare)、實施(implement)、善後(windup)和彙報(report)。

【代碼8-1】

這樣,就可以基於該模板完成一些具體的打掃衛生工作。例如,代碼8-2 和代碼8-3分別給出了擦玻璃和擦黑板的實現。

【代碼8-2】

【代碼8-3】

可以看到,雖然具體的行為不同,但是 WipeGlass和 WipeBlackboard兩者遵循的模板是一樣的,都是由 Cleaning類給定的。這就是模板模式的作用,即確定了一套操作的框架,而子類只需在此框架的基礎上定義具體的實現即可。

類型處理器

類型處理器的基類與實現類

作為一個 ORM框架,處理 Java對象和數據庫關係之間的映射是 MyBatis工作中的重要部分。然而在 Java中存在 Integer、String、Data等各種類型的數據,在數據庫中也存在varchar、longvarchar、tinyint等各種類型的數據。不同類型的字段所需的讀、寫方法各不相同,因此需要對不同類型的字段採取相應的處理方式。

例如,User中有如代碼8-4所示的幾個屬性。

【代碼8-4】

在對 User對象的屬性進行讀取和賦值時,需要用 Integer的相關處理方式來操作 id、sex屬性,而要用 String的相關處理方式來操作name、schoolName屬性。

在 type包中,將每種類型對應的處理方式封裝在了對應的類型處理器 TypeHandler中。例如,IntegerTypeHandler負責完成對 Integer類型的處理。

type 包共有 43 個類型處理器,這些類型處理器的名稱也均以「TypeHandler」結尾。而 TypeHandler和 BaseTypeHandler則分別是類型處理器接口和類型處理器基類。

圖8-1展示了type包中類型處理器相關的類圖。

圖8-1 類型處理器類圖

TypeHandler<T>是一個接口,其中定義了進行數據處理操作的幾個抽象方法。而BaseTypeHandler<T>繼承了 TypeHandler<T>接口,並實現了 TypeHandler<T>中的接口。

在類型處理器相關類的設計中採用了模板模式,BaseTypeHandler<T>作為所有類型處理器的基類,定義了模板的框架。而在各個具體的實現類中,則實現了具體的細節。

以代碼8-5展示的 BaseTypeHandler中 getResult(ResultSet,String)方法為例,該方法完成了異常處理等統一的工作,而與具體類型相關的 getNullableResult(ResultSet,String)操作則通過抽象方法交給具體的類型處理器實現。這就是典型的模板模式。

【代碼8-5】

BaseTypeHandler<T>交給具體的類型處理器實現的抽象方法一共只有四個。在每個類型處理器都需要實現這四個方法。· void setNonNullParameter(PreparedStatement,int,T,JdbcType):向 PreparedStatement對象中的指定變量位置寫入一個不為null的值;

· T getNullableResult(ResultSet,String):從 ResultSet中按照字段名讀出一個可能為null的數據;

· T getNullableResult(ResultSet,int):從 ResultSet 中按照字段編號讀出一個可能為null的數據;

· T getNullableResult(CallableStatement,int):從 CallableStatement中按照字段編號讀出一個可能為 null的數據。

因為上面的抽象方法跟具體的類型相關,因此存在泛型參數 T。在每種類型處理器中,都給出了泛型參數的值。以 IntegerTypeHandler為例,它設置泛型參數值為 Integer。IntegerTypeHandler類的源碼如代碼8-6所示。

【代碼8-6】

代碼8-6中,IntegerTypeHandler處理的類型是 Integer,這表明其 getNullableResult方法給出的就是一個 Integer結果。

TypeReference類

43個類型處理器可以處理不同 Java類型的數據,而這些類型處理器都是 TypeHandler接口的子類,因此可以都作為 TypeHandler來使用。

那會不會遇到一個問題,當 MyBatis取到某一個 TypeHandler時,卻不知道它到底是用來處理哪一種 Java類型的處理器?

為了解決這一問題,MyBatis 定義了一個 TypeReference 類。它能夠判斷出一個TypeHandler用來處理的目標類型。而它判斷的方法也很簡單:取出 TypeHandler實現類中的泛型參數 T的類型,這個值的類型也便是該 TypeHandler能處理的目標類型。該功能由getSuperclassTypeParameter方法實現,該方法能將找出的目標類型存入類中的 rawType屬性。getSuperclassTypeParameter方法帶注釋的源碼如代碼8-7所示。

【代碼8-7】

TypeReference 類是 BaseTypeHandler 的父類,因此所有的類型處理器都繼承了TypeReference 的功能。這意味着對任何一個類型處理器調用 getSuperclassTypeParameter方法,都可以得到該處理器用來處理的目標類型。

類型註冊表

定義了大量的類型處理器之後,MyBatis 還需要在遇到某種類型的數據時,快速找到與數據的類型對應的類型處理器。這個過程就需要各種類型註冊表的幫助。

type 包中的類型註冊表有三個:SimpleTypeRegistry、TypeAliasRegistry 和TypeHandlerRegistry。

SimpleTypeRegistry 是一個非常簡單的註冊表,其內部使用一個SIMPLE_TYPE_SET變量維護所有 Java基本類型。SIMPLE_TYPE_SET中的賦值是在 static代碼塊中進行的,如代碼8-8所示。這說明在 SimpleTypeRegistry初始化結束後,就已經將所有的 Java基本類型維護到了 SIMPLE_TYPE_SET中。

【代碼8-8】

TypeAliasRegistry是一個類型別名註冊表,其內部使用 typeAliases變量維護類型的別名與類型的對應關係。有了這個註冊表,我們就可以在很多場合使用類型的別名來指代具體的類型。TypeHandlerRegistry 是這三個註冊表中最為核心的一個,數據類型和相關處理器的對應關係就是由它維護的。

在介紹它之前,我們先介紹 Java數據類型和 JDBC數據類型。假設某個對象中存在一個「String name」屬性,則 name屬性在 Java中的數據類型是String。

然而它在數據庫中可能是 char、varchar,也可能是 tinytext、text 等多種類型。因此,Java數據類型和 JDBC數據類型並不是一對一的關係,而是一對多的關係。了解到這一點之後,我們在代碼8-9中直接給出 TypeHandlerRegistry類的帶注釋的屬性。

【代碼8-9】

了解了 TypeHandlerRegistry的屬性後,也可以猜測出如何才能拿到一個類型的類型處理器,實際就是一個兩次映射的過程。

· 根據傳入的 Java 類型,調用 getJdbcHandlerMap 子方法找尋對應的jdbcTypeHandlerMap後返回。· 基於 jdbcTypeHandlerMap,根據 JDBC類型找到對應的 TypeHandler。

例如,在給定 Java類型是 String,而 JDBC類型是 varchar後,就能唯一確定一個類型處理器。getTypeHandler 方法完成的就是這一過程,該方法帶注釋的源碼如代碼8-10所示。

【代碼8-10】

SimpleTypeRegistry、TypeAliasRegistry、TypeHandlerRegistry這三個類型註冊表的存在,使 MyBatis 不僅可以根據類型找尋其類型處理器,而且還可以根據類型別名找尋對應的類型處理器。

本文給大家講解的內容是通用源碼閱讀指導mybatis源碼詳解: type包

  1. 下篇文章給大家講解的是通用源碼閱讀指導mybatis源碼詳解: io包;
  2. 覺得文章不錯的朋友可以轉發此文關注小編;
  3. 感謝大家的支持