Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解

封面:洛小汐
做者:潘潘 java

在這裏插入圖片描述

Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解

作大事和作小事的難度是同樣的。二者都會消耗你的時間和精力,因此若是決心作事,就要作大事,要確保你的夢想值得追求,將來的收穫能夠配得上你的努力。mysql

Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解

前言

上一篇文章 《Mybatis系列全解(三):Mybatis簡單CRUD使用介紹》 ,咱們基本上手了 Mybatis 的增刪改查操做,也感覺到 Mybatis 的簡單高效舒美,可是確定有部分朋友對於 Mybatis 的配置文件只是瞭解基本組成和大體用法,尚無一套完整的結構記憶,因此本篇文章咱們將詳細的介紹 Mybatis 的配置全貌,畢竟 Mybatis 的配置文件對於整個 Mybatis 體系的構建與支撐有着深遠的影響。git

Mybatis系列全解腦圖分享,持續更新中github

在這裏插入圖片描述

Mybaits系列全解 (持續更新)

  • Mybatis系列全解(一):手寫一套持久層框架
  • Mybatis系列全解(二):Mybatis簡介與環境搭建
  • Mybatis系列全解(三):Mybatis簡單CRUD使用介紹
  • Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解
  • Mybatis系列全解(五):全網最全!詳解Mybatis的Mapper映射文件
  • Mybatis系列全解(六):Mybatis最硬核的API你知道幾個?
  • Mybatis系列全解(七):全息視角看Dao層兩種實現方式之傳統方式與代理方式
  • Mybatis系列全解(八):Mybatis的動態SQL
  • Mybatis系列全解(九):Mybatis的複雜映射
  • Mybatis系列全解(十):Mybatis註解開發
  • Mybatis系列全解(十一):Mybatis緩存全解
  • Mybatis系列全解(十二):Mybatis插件開發
  • Mybatis系列全解(十三):Mybatis代碼生成器
  • Mybatis系列全解(十四):Spring集成Mybatis
  • Mybatis系列全解(十五):SpringBoot集成Mybatis
  • Mybatis系列全解(十六):Mybatis源碼剖析

目錄

一、爲何要使用配置文件sql

二、Mybatis 配置全貌數據庫

三、XML 核心配置apache

四、XML 映射文件windows

五、總結數組

Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解

爲何要使用配置文件

試想,若是沒有配置文件,咱們的應用程序將只能沿着固定的姿態運行,幾乎不能作任何動態的調整,那麼這不是一套完美的設計,由於咱們但願擁有更寬更靈活的操做空間和更多的兼容度,同時也能解決硬編碼等問題,因此咱們須要有配置文件,對應用程序進行參數預設和設置初始化工做。緩存

那咱們爲什麼鍾情XML

首先,固然是 XML 配置文件自己就足夠優秀,格式規範,存儲小,跨平臺,讀取快...等等,所謂窈窕淑女,誰人不愛。

其次,也是一個重要影響因素,就是各大領域大佬的支持,像微軟、像Java系...等等,世上本無路,只是走的人多了,也就成了路 ( 這句話是魯迅老先生說的)。

因此,Mybatis選擇搭配XML配置,實屬合理。

Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解

Mybatis 配置全貌

Mybatis框架自己,理論上就一個配置文件,其實也只須要一個配置文件,即mybatis-config.xml (固然文件名容許自由命名),只不過這個配置文件其中的一個屬性mappers(映射器),因爲可能產生過多的SQL映射文件,因而咱們物理上單獨拓展出來,容許使用者定義任意數量的 xxxMapper.xml 映射文件。

把SQL映射文件單獨配置,是有好處的,一是靈活度上容許任意拓展,二也避免了其它無需常常變更的屬性配置遭遇誤改。

咱們看看Mybatis官網給出的配置文件層次結構:

  • configuration(配置)
    • properties(屬性)
    • settings(設置)
    • typeAliases(類型別名)
      • 三種別名定義方式
    • typeHandlers(類型處理器)
      • 自定義類型處理器
    • objectFactory(對象工廠)
    • plugins(插件)
    • environments(環境配置)
      • environment(環境變量)
        • transactionManager(事務管理器)
        • dataSource(數據源)
          • 三種支持數據源與自定義數據源
    • databaseIdProvider(數據庫廠商標識)
    • mappers(映射器)

實際配置文件XML內容以下,除了約束頭 <?xml> 與 <!DOCTYPE>,

其他標籤元素都是 Mybatis 的核心配置屬性 :

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!-- 一、屬性:例如jdbc.properties -->
    <properties resource="jdbc.properties"></properties>

    <!-- 二、設置:定義全局性設置,例如開啓二級緩存 -->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <!-- 三、類型名稱:爲一些類定義別名 -->
    <typeAliases>
        <typeAlias type="com.panshenlian.pojo.User" alias="user"></typeAlias>
    </typeAliases>

    <!-- 四、類型處理器:定義Java類型與數據庫中的數據類型之間的轉換關係 -->
    <typeHandlers></typeHandlers>

    <!-- 五、對象工廠 -->
    <objectFactory type=""></objectFactory>

    <!-- 六、插件:mybatis的插件,支持自定義插件 -->
    <plugins>
        <plugin interceptor=""></plugin>
    </plugins>

    <!-- 七、環境:配置mybatis的環境 -->
    <environments default="development">
        <!-- 環境變量:支持多套環境變量,例如開發環境、生產環境 -->
        <environment id="development">
            <!-- 事務管理器:默認JDBC -->
            <transactionManager type="JDBC" />
            <!-- 數據源:使用鏈接池,並加載mysql驅動鏈接數據庫 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
    </environments>

    <!-- 八、數據庫廠商標識 -->
    <databaseIdProvider type=""></databaseIdProvider>

    <!-- 九、映射器:指定映射文件或者映射類 -->
    <mappers>
        <mapper resource="UserMapper.xml" />
    </mappers>

</configuration>

必須注意:Mybatis配置文件的屬性位置順序是 固定 的,不容許 顛倒順序,不然 Mybatis 在解析 XML 文件的時候就會拋出異常,這個與 Mybatis 框架啓動加載配置信息順序有關,後續咱們源碼分析會講到。

以上基本可以清晰看明白 Mybatis 配置文件的層次結構關係,咱們簡單畫一張腦圖:

在這裏插入圖片描述

基本是須要咱們掌握 9 大頂級元素配置,其中標記 橘紅色 的屬性配置,因爲涉及 插件動態SQL ,插件配置能夠應用於分頁與功能加強等,動態SQL例如 if 標籤、where 標籤、foreach標籤等,初步理解爲應用於SQL語句拼接。這兩塊屬於 Mybatis 的兩個特性,咱們後續單獨詳細進行梳理討論。

Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解

XML 核心配置

咱們的核心配置文件 configuration(配置)做爲最頂級節點,其他 9 大屬性都必須嵌套在其內,對於內部 9 大節點,咱們逐一講解:

一、properties(屬性)

屬性標籤,顯而易見就是提供屬性配置,可進行動態替換,通常能夠在 Java 屬性文件中配置,例如 jdbc.properties 配置文件 ,或經過 properties 元素標籤中的子元素 property 來指定配置。

舉例咱們須要配置數據源信息,採用 property 標籤能夠這樣配置:

<properties>
  <property name="driver" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://127.0.0.1:3306/myDB"/>
  <property name="username" value="user1"/>
  <property name="password" value="123456"/>
</properties>

設置好的屬性能夠在整個配置文件中用來替換須要動態配置的屬性值。好比:

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

或者咱們使用 Java 中的屬性配置文件,把屬性配置元素具體化到一個屬性文件中,而且使用屬性文件的 key 名做爲佔位符。例如 jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc\:mysql\://127.0.0.1\:3306/myDB
username=root
password=123456

使用時咱們把屬性文件引入,並使用文件中定義的佔位符,例如 db.driver :

<!-- 引入屬性配置文件 -->
<properties resource="jdbc.properties"></properties>

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

在這裏插入圖片描述

可是問題來了,當咱們既使用 *.properties 配置文件,同時又設置了 property 元素值,Mybatis 會使用哪邊配置的屬性值呢? 例如這種狀況 :

<properties resource="jdbc.properties"> 
  <property name="driver" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://127.0.0.1:3306/myDB"/>
  <property name="username" value="user1"/>
  <property name="password" value="123456"/>
</properties>

這裏,若是在 property 標籤元素與 jdbc.properties 文件中同時存在相同屬性,那麼屬性文件將會覆蓋 property 標籤元素的屬性,例如最終 username屬性值會使用 jdbc.properties 文件中設置的 root,而不會使用屬性元素設置的 user1 。這樣實際爲配置提供了諸多靈活選擇。

另外,properties 元素容許配置 resource 屬性或 url 屬性,只能二選一,要麼使用 resource 指定本地的配置文件,要麼使用 url 指定遠程的配置文件,由於 Mybatis 在加載配置時,若是發現 url 與 resource 同時存在,會拋出異常禁止。

<!-- 配置resource-->
<properties resource="xxx.properties">
    <property name="driver" value="com.mysql.jdbc.Driver"/>
</properties>

<!-- 配置url-->
<properties url="http://xxxx">
    <property name="driver" value="com.mysql.jdbc.Driver"/>
</properties>

還有一種狀況,像 Mybatis 在解析配置的時候,也能夠在 Java 代碼中構建屬性 java.util.Properties 屬性對象並傳遞到 SqlSessionFactoryBuilder.build() 方法中,例如:

// 構建屬性對象
Properties props = new Properties();
props.setProperty("driver","com.mysql.jdbc.Driver"); 
props.setProperty("url","jdbc:mysql://127.0.0.1:3306/myDB"); 
props.setProperty("username","user1"); 
props.setProperty("password","123456");  

// 傳遞屬性構建 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);

那麼這三種方式都容許配置,那在屬性配置重複的狀況下,優先級別是怎樣呢?

properties 優先級

一、第一優先級:在 Java 代碼中構建的 properties 屬性對象;

二、第二優先級:經過屬性 resource 或 url 讀取到的本地文件或遠程文件;

三、第三優先級:直接在 properties 內部子標籤元素 property 中設置的屬性。

注意,在實際開發中,爲了不給後期維護形成困擾,建議使用單一種配置方式。

二、settings(設置)

settings 標籤元素,是 MyBatis 中極爲重要的調整設置,它們會動態改變 MyBatis 的運行時行爲,這些配置就像 Mybatis 內置的許多功能,當你須要使用時能夠根據須要靈活調整,而且 settings 能配置的東西特別多,咱們先來一塊兒看看,一個完整的屬性配置示例:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
  <... more .../>
</settings>
  • 屬性cacheEnabled

    • 全局性地開啓或關閉全部映射器配置文件中已配置的任何緩存
    • 支持 true | false
    • 默認 true
  • 屬性lazyLoadingEnabled

    • 延遲加載的全局開關。當開啓時,全部關聯對象都會延遲加載。 特定關聯關係中可經過設置 fetchType 屬性來覆蓋該項的開關狀態。
    • 支持 true | false
    • 默認 false
  • 屬性 aggressiveLazyLoading

    • 開啓時,任一方法的調用都會加載該對象的全部延遲加載屬性。 不然,每一個延遲加載屬性會按需加載(參考 lazyLoadTriggerMethods)。
    • 支持 true | false
    • 默認 false (在 3.4.1 及以前的版本中默認爲 true)
  • 屬性 multipleResultSetsEnabled

    • 是否容許單個語句返回多結果集(須要數據庫驅動支持)。
    • 支持 true | false
    • 默認 true
  • 屬性 useColumnLabel

    • 使用列標籤代替列名。實際表現依賴於數據庫驅動,具體可參考數據庫驅動的相關文檔,或經過對比測試來觀察。
    • 支持 true | false
    • 默認 true
  • 屬性 useGeneratedKeys

    • 容許 JDBC 支持自動生成主鍵,須要數據庫驅動支持。若是設置爲 true,將強制使用自動生成主鍵。儘管一些數據庫驅動不支持此特性,但仍可正常工做(如 Derby)。
    • 支持 true | false
    • 默認 false
  • 屬性 autoMappingBehavior

    • 指定 MyBatis 應如何自動映射列到字段或屬性。 NONE 表示關閉自動映射;PARTIAL 只會自動映射沒有定義嵌套結果映射的字段。 FULL 會自動映射任何複雜的結果集(不管是否嵌套)。
    • 支持 NONE, PARTIAL, FULL
    • 默認 PARTIAL
  • 屬性 autoMappingUnknownColumnBehavior

    • 指定發現自動映射目標未知列(或未知屬性類型)的行爲。
      • NONE: 不作任何反應
      • WARNING: 輸出警告日誌( org.apache.ibatis.session.AutoMappingUnknownColumnBehavior 的日誌等級必須設置爲 WARN)
      • FAILING: 映射失敗 (拋出 SqlSessionException)
    • 支持 NONE, WARNING, FAILING
    • 默認 NONE
  • 屬性 defaultExecutorType

    • 配置默認的執行器。SIMPLE 就是普通的執行器;REUSE 執行器會重用預處理語句(PreparedStatement); BATCH 執行器不只重用語句還會執行批量更新。
    • 支持 SIMPLE REUSE BATCH
    • 默認 SIMPLE
  • 屬性 defaultStatementTimeout

    • 設置超時時間,它決定數據庫驅動等待數據庫響應的秒數。
    • 支持 任意正整數
    • 默認 未設置 (null)
  • 屬性 defaultFetchSize

    • 動的結果集獲取數量(fetchSize)設置一個建議值。此參數只能夠在查詢設置中被覆蓋。
    • 支持 任意正整數
    • 默認 未設置 (null)
  • 屬性 defaultResultSetType

    • 指定語句默認的滾動策略。(新增於 3.5.2)
    • 支持 FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同於未設置)
    • 默認 未設置 (null)
  • 屬性 safeRowBoundsEnabled

    • 是否容許在嵌套語句中使用分頁(RowBounds)。若是容許使用則設置爲 false。
    • 支持 true | false
    • 默認 false
  • 屬性 safeResultHandlerEnabled

    • 是否容許在嵌套語句中使用結果處理器(ResultHandler)。若是容許使用則設置爲 false。
    • 支持 true | false
    • 默認 true
  • 屬性 mapUnderscoreToCamelCase

    • 是否開啓駝峯命名自動映射,即從經典數據庫列名 A_COLUMN 映射到經典 Java 屬性名 aColumn。
    • 支持 true | false
    • 默認 false
  • 屬性 localCacheScope

    • MyBatis 利用本地緩存機制(Local Cache)防止循環引用和加速重複的嵌套查詢。 默認值爲 SESSION,會緩存一個會話中執行的全部查詢。 若設置值爲 STATEMENT,本地緩存將僅用於執行語句,對相同 SqlSession 的不一樣查詢將不會進行緩存。
    • 支持 SESSION | STATEMENT
    • 默認 SESSION
  • 屬性 jdbcTypeForNull

    • 當沒有爲參數指定特定的 JDBC 類型時,空值的默認 JDBC 類型。 某些數據庫驅動須要指定列的 JDBC 類型,多數狀況直接用通常類型便可,好比 NULL、VARCHAR 或 OTHER。
    • JdbcType 常量,經常使用值:NULL、VARCHAR 或 OTHER。
    • 默認 OTHER
  • 屬性 lazyLoadTriggerMethods

    • 指定對象的哪些方法觸發一次延遲加載。
    • 支持 用逗號分隔的方法列表。
    • 默認 equals,clone,hashCode,toString
  • 屬性 defaultScriptingLanguage

    • 指定動態 SQL 生成使用的默認腳本語言。
    • 支持 一個類型別名或全限定類名。
    • 默認 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
  • 屬性 defaultEnumTypeHandler

    • 指定 Enum 使用的默認 TypeHandler 。(新增於 3.4.5)
    • 支持 一個類型別名或全限定類名。
    • 默認 org.apache.ibatis.type.EnumTypeHandler
  • 屬性 callSettersOnNulls

    • 指定當結果集中值爲 null 的時候是否調用映射對象的 setter(map 對象時爲 put)方法,這在依賴於 Map.keySet() 或 null 值進行初始化時比較有用。注意基本類型(int、boolean 等)是不能設置成 null 的。
    • 支持 true | false
    • 默認 false
  • 屬性 returnInstanceForEmptyRow

    • 當返回行的全部列都是空時,MyBatis默認返回 null。 當開啓這個設置時,MyBatis會返回一個空實例。 請注意,它也適用於嵌套的結果集(如集合或關聯)。(新增於 3.4.2)
    • 支持 true | false
    • 默認 false
  • 屬性 logPrefix

    • 指定 MyBatis 增長到日誌名稱的前綴。
    • 支持 任何字符串
    • 默認 未設置
  • 屬性 logImpl

    • 指定 MyBatis 所用日誌的具體實現,未指定時將自動查找。
    • 支持 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
    • 默認 未設置
  • 屬性 proxyFactory

    • 指定 Mybatis 建立可延遲加載對象所用到的代理工具。
    • 支持 CGLIB | JAVASSIST
    • 默認 JAVASSIST (MyBatis 3.3 以上
  • 屬性 vfsImpl

    • 指定 VFS 的實現
    • 支持 自定義 VFS 的實現的類全限定名,以逗號分隔。
    • 默認 未設置
  • 屬性 useActualParamName

    • 容許使用方法簽名中的名稱做爲語句參數名稱。 爲了使用該特性,你的項目必須採用 Java 8 編譯,而且加上 -parameters 選項。(新增於 3.4.1)
    • 支持 true | false
    • 默認 true
  • 屬性 configurationFactory

    • 指定一個提供 Configuration 實例的類。 這個被返回的 Configuration 實例用來加載被反序列化對象的延遲加載屬性值。 這個類必須包含一個簽名爲static Configuration getConfiguration() 的方法。(新增於 3.2.3)
    • 支持 一個類型別名或徹底限定類名。
    • 默認 未設置
  • 屬性 shrinkWhitespacesInSql

    • 從SQL中刪除多餘的空格字符。請注意,這也會影響SQL中的文字字符串。 (新增於 3.5.5)
    • 支持 true | false
    • 默認 false
  • 屬性 defaultSqlProviderType
    • 指定一個自己擁查詢方法的類( 從 3.5.6 開始 ),這個類能夠配置在註解 @SelectProvider 的 type 屬性值上。
    • 支持 一個類型別名或徹底限定類名。
    • 默認 未設置

settings 支持了特別多功能支持,其實常規開發中使用到的屬性項不會特別多,除非項目有特殊要求,因此建議你們把這些設置當作字典便可,沒必要詳記 每個屬性使用,須要時翻閱研讀。

三、typeAliases(類型別名)

類型別名能夠給 Java 類型設置一個簡稱。 它僅用於 XML 配置,意在下降冗餘的全限定類名書寫,由於書寫類的全限定名太長了,咱們但願有一個簡稱來指代它。類型別名在 Mybatis 中分爲 系統內置用戶自定義 兩類,Mybatis 會在解析配置文件時把 typeAliases 實例存儲進入 Configuration 對象中,須要使用時直接獲取。

通常咱們能夠自定義別名,例如:

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/> 
</typeAliases>

像這樣配置時,咱們就能夠在任何須要使用 domain.blog.Author 的地方,直接使用別名 author 。

可是,若是遇到項目中特別多 Java 類須要配置別名,怎麼更快的設置呢?

能夠指定一個包名進行掃描,MyBatis 會在包名下面掃描須要的 Java Bean,好比:

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

每個在包 domain.blog 中的 Java Bean,在沒有註解的狀況下,會使用 Bean 的首字母小寫的非限定類名來做爲它的別名。 好比 domain.blog.Author 的別名爲 author;如有 註解 ,則別名爲其自定義的註解值。見下面的例子:

@Alias("myAuthor")
public class Author {
    ...
}

Mybatis 已經爲許多常見的 Java 類型內建了相應的類型別名。下面就是一些爲常見的 Java 類型內建的類型別名。它們都是不區分大小寫的,注意,爲了應對原始類型的命名重複,採起了特殊的命名風格,能夠發現 基本類型 的別名前綴都有下劃線 ‘_’,而基本類型的 包裝類 則沒有,這個須要注意:

  • 別名 _byte,對應的類型是:byte
  • 別名 _long,對應的類型是:long
  • 別名 _short,對應的類型是:short
  • 別名 _int,對應的類型是:int
  • 別名 _integer,對應的類型是:int
  • 別名 _double,對應的類型是:double
  • 別名 _float,對應的類型是:float
  • 別名 _boolean,對應的類型是:boolean
  • 別名 string,對應的類型是:String
  • 別名 byte,對應的類型是:Byte
  • 別名 long,對應的類型是:Long
  • 別名 short,對應的類型是:Short
  • 別名 int,對應的類型是:Integer
  • 別名 integer,對應的類型是:Integer
  • 別名 double,對應的類型是:Double
  • 別名 float,對應的類型是:Float
  • 別名 boolean,對應的類型是:Boolean
  • 別名 date,對應的類型是:Date
  • 別名 decimal,對應的類型是:BigDecimal
  • 別名 bigdecimal,對應的類型是:BigDecimal
  • 別名 object,對應的類型是:Object
  • 別名 map,對應的類型是:Map
  • 別名 hashmap,對應的類型是:HashMap
  • 別名 list,對應的類型是:List
  • 別名 arraylist,對應的類型是:ArrayList
  • 別名 collection,對應的類型是:Collection
  • 別名 iterator,對應的類型是:Iterator

咱們能夠經過源碼查看內置的類型別名的註冊信息。

具體源碼路徑在 org.apache.ibatis.type.TypeAliasRegistry # TypeAliasRegistry() :

public TypeAliasRegistry() {
    registerAlias("string", String.class);

    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);

    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);

    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);

    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);

    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);

    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);

    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);

    registerAlias("ResultSet", ResultSet.class);
  }

別名是不區分大小寫的,同時也支持數組類型,只須要加 「[]」 便可使用,好比 Long 數組別名咱們能夠用 long[] 直接代替,例如在實際開發中,int 、INT 、integer 、INTEGER 都是表明 Integer , 這裏主要因爲 MyBatis 在註冊別名的時候會所有轉爲小寫字母進行存儲,另外以上列表 無需牢記,僅僅在須要使用的時候查閱便可,基本也均可以看得明白。

四、typeHandlers(類型處理器)

MyBatis 在設置預處理SQL語句(PreparedStatement)中所須要的 參數 或從 結果集 ResultSet 中獲取對象時, 都會用類型處理器將獲取到的值以合適的方式轉換成 Java 類型。

在這裏插入圖片描述

類型處理器,主要用於處理 Java 類型與 JDBC 類型的映射匹配關係處理,下表描述了一些默認的類型處理器。

  • 類型處理器 BooleanTypeHandler
    • Java 類型:java.lang.Boolean, boolean
    • JDBC 類型:數據庫兼容的 BOOLEAN
  • 類型處理器 ByteTypeHandler
    • Java 類型:java.lang.Byte, byte
    • JDBC 類型:數據庫兼容的 NUMERIC 或 BYTE
  • 類型處理器 ShortTypeHandler
    • Java 類型:java.lang.Short, short
    • JDBC 類型:數據庫兼容的 NUMERIC 或 SMALLINT
  • 類型處理器 IntegerTypeHandler
    • Java 類型:java.lang.Integer, int
    • JDBC 類型:數據庫兼容的 NUMERIC 或 INTEGER
  • 類型處理器 LongTypeHandler
    • Java 類型:java.lang.Long, long
    • JDBC 類型:數據庫兼容的 NUMERIC 或 BIGINT
  • 類型處理器 FloatTypeHandler
    • Java 類型:java.lang.Float, float
    • JDBC 類型:數據庫兼容的 NUMERIC 或 FLOAT
  • 類型處理器 DoubleTypeHandler
    • Java 類型:java.lang.Double, double
    • JDBC 類型:數據庫兼容的 NUMERIC 或 DOUBLE
  • 類型處理器 BigDecimalTypeHandler
    • Java 類型:java.math.BigDecimal
    • JDBC 類型:數據庫兼容的 NUMERIC 或 DECIMAL
  • 類型處理器 StringTypeHandler
    • Java 類型:java.lang.String
    • JDBC 類型:CHAR, VARCHAR
  • 類型處理器 ClobReaderTypeHandler
    • Java 類型:java.io.Reader
    • JDBC 類型:-
  • 類型處理器 ClobTypeHandler
    • Java 類型:java.lang.String
    • JDBC 類型:CLOB, LONGVARCHAR
  • 類型處理器 NStringTypeHandler
    • Java 類型:java.lang.String
    • JDBC 類型:NVARCHAR, NCHAR
  • 類型處理器 NClobTypeHandler
    • Java 類型:java.lang.String
    • JDBC 類型:NCLOB
  • 類型處理器 BlobInputStreamTypeHandler
    • Java 類型:java.io.InputStream
    • JDBC 類型:-
  • 類型處理器 ByteArrayTypeHandler
    • Java 類型:byte[]
    • JDBC 類型:數據庫兼容的字節流類型
  • 類型處理器 BlobTypeHandler
    • Java 類型:byte[]
    • JDBC 類型:BLOB, LONGVARBINARY
  • 類型處理器 DateTypeHandler
    • Java 類型:java.util.Date
    • JDBC 類型:TIMESTAMP
  • 類型處理器 DateOnlyTypeHandler
    • Java 類型:java.util.Date
    • JDBC 類型:DATE
  • 類型處理器 TimeOnlyTypeHandler
    • Java 類型:java.util.Date
    • JDBC 類型:TIME
  • 類型處理器 SqlTimestampTypeHandler
    • Java 類型:java.sql.Timestamp
    • JDBC 類型:TIMESTAMP
  • 類型處理器 SqlDateTypeHandler
    • Java 類型:java.sql.Date
    • JDBC 類型:DATE
  • 類型處理器 SqlTimeTypeHandler
    • Java 類型:java.sql.Time
    • JDBC 類型:TIME
  • 類型處理器 ObjectTypeHandler
    • Java 類型:Any
    • JDBC 類型:OTHER 或未指定類型
  • 類型處理器 EnumTypeHandler
    • Java 類型:Enumeration Type
    • JDBC 類型:VARCHAR 或任何兼容的字符串類型,用來存儲枚舉的名稱(而不是索引序數值)
  • 類型處理器 EnumOrdinalTypeHandler
    • Java 類型:Enumeration Type
    • JDBC 類型:任何兼容的 NUMERIC 或 DOUBLE 類型,用來存儲枚舉的序數值(而不是名稱)。
  • 類型處理器 SqlxmlTypeHandler
    • Java 類型:java.lang.String
    • JDBC 類型:SQLXML
  • 類型處理器 InstantTypeHandler
    • Java 類型:java.time.Instant
    • JDBC 類型:TIMESTAMP
  • 類型處理器 LocalDateTimeTypeHandler
    • Java 類型:java.time.LocalDateTime
    • JDBC 類型:TIMESTAMP
  • 類型處理器 LocalDateTypeHandler
    • Java 類型:java.time.LocalDate
    • JDBC 類型:DATE
  • 類型處理器 LocalTimeTypeHandler
    • Java 類型:java.time.LocalTime
    • JDBC 類型:TIME
  • 類型處理器 OffsetDateTimeTypeHandler
    • Java 類型:java.time.OffsetDateTime
    • JDBC 類型:TIMESTAMP
  • 類型處理器 OffsetTimeTypeHandler
    • Java 類型:java.time.OffsetTime
    • JDBC 類型:TIME
  • 類型處理器 ZonedDateTimeTypeHandler
    • Java 類型:java.time.ZonedDateTime
    • JDBC 類型:TIMESTAMP
  • 類型處理器 YearTypeHandler
    • Java 類型:java.time.Year
    • JDBC 類型:INTEGER
  • 類型處理器 MonthTypeHandler
    • Java 類型:java.time.Month
    • JDBC 類型:INTEGER
  • 類型處理器 YearMonthTypeHandler
    • Java 類型:java.time.YearMonth
    • JDBC 類型:VARCHAR 或 LONGVARCHAR
  • 類型處理器 JapaneseDateTypeHandler
    • Java 類型:java.time.chrono.JapaneseDate
    • JDBC 類型:DATE

咱們能夠經過源碼查看內置的類型別名的註冊信息。

具體源碼路徑在 org.apache.ibatis.type.TypeHandlerRegistry # TypeHandlerRegistry() :

public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());

    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());

    register(Reader.class, new ClobReaderTypeHandler());
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());

    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());

    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());

    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());

    register(Object.class, UNKNOWN_TYPE_HANDLER);
    register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
    register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);

    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());

    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

    // mybatis-typehandlers-jsr310
    if (Jdk.dateAndTimeApiExists) {
      Java8TypeHandlersRegistrar.registerDateAndTimeHandlers(this);
    }

    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
  }

從 3.4.5 開始,MyBatis 默認支持 JSR-310(日期和時間 API) ,能夠在以上源碼上看到新增支持。

通常,你能夠重寫已有的類型處理器

或根據業務須要建立你本身的類型處理器,

以處理不支持的類型或非標準的類型。

具體作法爲:

一、實現 org.apache.ibatis.type.TypeHandler 接口;

二、繼承 org.apache.ibatis.type.BaseTypeHandler 類。

自己 BaseTypeHandler 類做爲抽象類就已經實現了 TypeHandler 接口。

因此咱們看到接口 TypeHandler 定義了四個方法:

public interface TypeHandler<T> {

  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

從方法名 setParametergetResult 咱們就能夠知道,是發生在預編譯時設置參數(增刪改查傳入參數)與查詢結果集後轉換爲 Java 類型時,類型處理器發揮做用。

具體實現以下,先自定義類型處理器類 MyExampleTypeHandler :

// MyExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyExampleTypeHandler extends BaseTypeHandler<String> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
    ps.setString(i, parameter);
  }

  @Override
  public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
    return rs.getString(columnName);
  }

  @Override
  public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    return rs.getString(columnIndex);
  }

  @Override
  public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    return cs.getString(columnIndex);
  }
}

自定義類已設定:JdbcType.VARCHAR 與 String 類作映射轉換(註解和泛型已體現)。

其次,在覈心配置文件中設置類型處理器:

<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.mybatis.example.MyExampleTypeHandler"/>
</typeHandlers>

或者不使用註解方式的話,取消 @MappedJdbcTypes(JdbcType.VARCHAR) 註解,直接在 xml 配置中指定 jdbcType 與 javaType 映射 :

<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler jdbcType="VARCHAR" javaType="string" handler="org.mybatis.example.MyExampleTypeHandler"/>
</typeHandlers>

記住, typeHandler 的配置方式優先級高於註解配置方式。

這裏,自定義類型處理器將會覆蓋已有的處理 Java String 類型的屬性以及 VARCHAR 類型的參數和結果的類型處理器,基本以上步驟就已經自定了 JdbcType.VARCHARString類作映射轉換。

其實到這裏,咱們基本也就完成了類型處理器的自定義轉換,可是有一種狀況,就是咱們但願咱們自定義的類型處理器只處理某一個 Java 實體中的 JdbcType.VARCHAR 與 String 類映射轉換,其它實體的處理仍是使用系統內置的轉換,很簡單,咱們只須要把以上兩步都去掉,在自定義類型處理類的註解@javaType和@MappedJdbcTypes都移除,配置文件中把 typehandler 屬性配置移除,直接在映射文件中編寫:

<resultMap id="MyResultMap" type="com.panshenlian.pojo.User">
        <!-- id爲int類型,可是沒指定自定義類型處理器,不受影響-->
        <id column="id" property="id" />
        <!-- username爲String類型,可是沒指定自定義類型處理器,不受影響-->
        <id column="username" property="username" />
        <!-- password爲String類型,可是沒指定自定義類型處理器,不受影響-->
        <id column="password" property="password" />

        <!-- birthday爲String類型,指定自定義類型處理器,受影響!-->
        <id column="birthday" property="birthday"  typeHandler="com.panshenlian.typeHandler.MyStringHandler"/>

    </resultMap>
    <select id="findAll" resultType="com.panshenlian.pojo.User" resultMap="MyResultMap">
        select * from User
    </select>

User 實體參考:

package com.panshenlian.pojo;

/**
 * @Author: panshenlian
 * @Description: 用戶實體
 * @Date: Create in 2:08 2020/12/07
 */
public class User {
    private int id;
    private String username;
    private String password;
    private String birthday;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    } 
}

最終自定義類型處理器,只會對 birthday 字段產生影響,其他字段均不受影響。

自定義類型處理器很靈活,只有當指定對應的 Java 類型和 Jdbc 類型時,處理器纔會具體生效,不然 Mybatis 會默認匹配系統內置的類型處理器。

另外,當咱們自定義不少類型處理器時,系統支持配置包掃描的方式查找類型處理器:

<!-- mybatis-config.xml -->
<typeHandlers>
  <package name="org.mybatis.example"/>
</typeHandlers>

注意在使用自動發現功能的時候,只能經過註解方式來指定 JDBC 的類型。

你能夠建立可以處理多個類的泛型類型處理器。爲了使用泛型類型處理器, 須要增長一個接受該類的 class 做爲參數的構造器,這樣 MyBatis 會在構造一個類型處理器實例的時候傳入一個具體的類。

//GenericTypeHandler.java
public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E> {

  private Class<E> type;

  public GenericTypeHandler(Class<E> type) {
    if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
    this.type = type;
  }
  ...

處理枚舉類型

若想映射枚舉類型 Enum,則須要從 EnumTypeHandler 或者 EnumOrdinalTypeHandler 中選擇一個來使用。

好比說咱們想存儲取近似值時用到的舍入模式。默認狀況下,MyBatis 會利用 EnumTypeHandler 來把 Enum 值轉換成對應的名字。

注意 EnumTypeHandler 在某種意義上來講是比較特別的,其它的處理器只針對某個特定的類,而它不一樣,它會處理任意繼承了 Enum 的類。

不過,咱們可能不想存儲名字,相反咱們的 DBA 會堅持使用整形值代碼。那也同樣簡單:在配置文件中把 EnumOrdinalTypeHandler 加到 typeHandlers 中便可, 這樣每一個 RoundingMode 將經過他們的序數值來映射成對應的整形數值。

<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/>
</typeHandlers>

但要是你想在一個地方將 Enum 映射成字符串,在另一個地方映射成整形值呢?

自動映射器(auto-mapper)會自動地選用 EnumOrdinalTypeHandler 來處理枚舉類型, 因此若是咱們想用普通的 EnumTypeHandler,就必需要顯式地爲那些 SQL 語句設置要使用的類型處理器。

下一篇文章咱們纔開始介紹映射器 mapper.xml 文件,若是你首次閱讀映射器概念,可能須要先跳過這裏先去了解 mapper.xml 文件配置,再回頭過來看。

<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.apache.ibatis.submitted.rounding.Mapper">
    <resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="funkyNumber" property="funkyNumber"/>
        <result column="roundingMode" property="roundingMode"/>
    </resultMap>

    <select id="getUser" resultMap="usermap">
        select * from users
    </select>
    <insert id="insert">
        insert into users (id, name, funkyNumber, roundingMode) values (
            #{id}, #{name}, #{funkyNumber}, #{roundingMode}
        )
    </insert>

    <resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap2">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="funkyNumber" property="funkyNumber"/>
        <result column="roundingMode" property="roundingMode" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/>
    </resultMap>
    <select id="getUser2" resultMap="usermap2">
        select * from users2
    </select>
    <insert id="insert2">
        insert into users2 (id, name, funkyNumber, roundingMode) values (
            #{id}, #{name}, #{funkyNumber}, #{roundingMode, typeHandler=org.apache.ibatis.type.EnumTypeHandler}
        )
    </insert>

</mapper>

注意,這裏的 select 語句強制使用 resultMap 來代替 resultType

五、objectFactory(對象工廠)

每次 MyBatis 建立結果對象的新實例時,它都會使用一個對象工廠(ObjectFactory)實例來完成實例化工做。 默認的對象工廠須要作的僅僅是實例化目標類,要麼經過默認無參構造方法,要麼經過存在的參數映射來調用帶有參數的構造方法。 若是想覆蓋對象工廠的默認行爲,能夠經過建立本身的對象工廠來實現。好比:

// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
      public Object create(Class type) {
        return super.create(type);
      }
      public Object create(Class type, List constructorArgTypes, List constructorArgs) {
        return super.create(type, constructorArgTypes, constructorArgs);
      }
      public void setProperties(Properties properties) {
        super.setProperties(properties);
      }
      public  boolean isCollection(Class type) {
        return Collection.class.isAssignableFrom(type);
      }
}
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
  <property name="someProperty" value="100"/>
</objectFactory>

ObjectFactory 接口很簡單,它包含兩個建立用的方法,一個是處理默認構造方法的,另一個是處理帶參數的構造方法的。 最後,setProperties 方法能夠被用來配置 ObjectFactory,在初始化你的 ObjectFactory 實例後, objectFactory 元素體中定義的屬性會被傳遞給 setProperties 方法。

正常狀況下咱們不須要使用到,或者說不建議使用,除非業務上確實須要對一個特殊實體初始構造作一個默認屬性值配置等處理,其他狀況不推薦使用,避免產生不可控風險。

六、plugins(插件)

MyBatis 容許你在映射語句執行過程當中的某一點進行攔截調用。默認狀況下,MyBatis 容許使用插件來攔截的方法調用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

插件功能主要開放攔截的對象就是以上列舉的 Mybatis 四大組件,後續咱們講 Mybatis 核心API 的時候或者單獨介紹自定義插件的時候會詳細說明,這裏你們能夠先大體瞭解,包括數據分頁、操做日誌加強、sql 性能監控等均可以經過插件實現,不過會存儲改造的風險,畢竟這些都是核心的 API 。

這四大類中方法具體能夠經過查看每一個方法的簽名來發現,或者直接查看 MyBatis 發行包中的源代碼。 若是你想作的不只僅是監控方法的調用,那麼你最好至關了解要重寫的方法的行爲。 由於在試圖修改或重寫已有方法的行爲時,極可能會破壞 MyBatis 的核心模塊。 這些都是更底層的類和方法,因此使用插件的時候要特別小心。

經過 MyBatis 提供的強大機制,使用插件是很是簡單的,只需實現 Interceptor 接口,並指定想要攔截的類,方法,參數(因爲有多態的狀況)便可。

// ExamplePlugin.java
@Intercepts({
    @Signature(
        type= Executor.class,
        method = "update",
        args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre processing if need
    Object returnObject = invocation.proceed();
    // implement post processing if need
    return returnObject;
  }
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}
<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

上面的插件將會攔截在 Executor 實例中全部的 「update」 方法調用, 這裏的 Executor 是負責執行底層映射語句的內部對象。

覆蓋配置類 「 謹慎使用,存在風險 」

除了用插件來修改 MyBatis 核心行爲之外,還能夠經過徹底覆蓋配置類來達到目的。只需繼承配置類後覆蓋其中的某個方法,再把它傳遞到 SqlSessionFactoryBuilder.build(myConfig) 方法便可。再次重申,這可能會極大影響 MyBatis 的行爲,務請慎之又慎。

七、environments(環境配置)

MyBatis 能夠配置成適應多種環境,這種機制有助於將 SQL 映射應用於多種數據庫之中, 現實狀況下有多種理由須要這麼作。例如,開發、測試和生產環境須要有不一樣的配置;或者想在具備相同 Schema 的多個生產數據庫中使用相同的 SQL 映射。還有許多相似的使用場景。

不過要記住:儘管能夠配置多個環境,但每一個 SqlSessionFactory 實例只能選擇一種環境。

因此,若是你想鏈接兩個數據庫,就須要建立兩個 SqlSessionFactory 實例,每一個數據庫對應一個。而若是是三個數據庫,就須要三個實例,依此類推,記起來很簡單:

每一個數據庫對應一個 SqlSessionFactory 實例。

爲了指定建立哪一種環境,只要將它做爲可選的參數傳遞給 SqlSessionFactoryBuilder 便可。能夠接受環境配置的兩個方法簽名是:

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);

若是忽略了環境參數,那麼將會加載默認環境,以下所示:

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);

environments 元素定義瞭如何配置環境。

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>

注意一些關鍵點:

  • 默認使用的環境 ID(好比:default="development")。
  • 每一個 environment 元素定義的環境 ID(好比:id="development")。
  • 事務管理器的配置(好比:type="JDBC")。
  • 數據源的配置(好比:type="POOLED")。

默認環境和環境 ID 顧名思義。 環境能夠隨意命名,但務必保證默認的環境 ID 要匹配其中一個環境 ID。

事務管理器(transactionManager)

在 MyBatis 中有兩種類型的事務管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC – 這個配置直接使用了 JDBC 的提交和回滾設施,它依賴從數據源得到的鏈接來管理事務做用域。
  • MANAGED – 這個配置幾乎沒作什麼。它從不提交或回滾一個鏈接,而是讓容器來管理事務的整個生命週期(好比 JEE 應用服務器的上下文)。 默認狀況下它會關閉鏈接。然而一些容器並不但願鏈接被關閉,所以須要將 closeConnection 屬性設置爲 false 來阻止默認的關閉行爲。例如:
<transactionManager type="MANAGED">
  <property name="closeConnection" value="false"/>
</transactionManager>

若是你正在使用 Spring + MyBatis,則沒有必要配置事務管理器,由於 Spring 模塊會使用自帶的管理器來覆蓋前面的配置。這兩種事務管理器類型都不須要設置任何屬性。它們實際上是類型別名,換句話說,你能夠用 TransactionFactory 接口實現類的全限定名或類型別名代替它們。

public interface TransactionFactory {
  default void setProperties(Properties props) { // 從 3.5.2 開始,該方法爲默認方法
    // 空實現
  }
  Transaction newTransaction(Connection conn);
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

在事務管理器實例化後,全部在 XML 中配置的屬性將會被傳遞給 setProperties() 方法。你的實現還須要建立一個 Transaction 接口的實現類,這個接口也很簡單:

public interface Transaction {
  Connection getConnection() throws SQLException;
  void commit() throws SQLException;
  void rollback() throws SQLException;
  void close() throws SQLException;
  Integer getTimeout() throws SQLException;
}

使用這兩個接口,你能夠徹底自定義 MyBatis 對事務的處理。

數據源(dataSource)

dataSource 元素使用標準的 JDBC 數據源接口來配置 JDBC 鏈接對象的資源。

大多數 MyBatis 應用程序會按示例中的例子來配置數據源。雖然數據源配置是可選的,但若是要啓用延遲加載特性,就必須配置數據源。

有三種內建的數據源類型(也就是 type="[UNPOOLED|POOLED|JNDI]"):

UNPOOLED– 這個數據源的實現會每次請求時打開和關閉鏈接。雖然有點慢,但對那些數據庫鏈接可用性要求不高的簡單應用程序來講,是一個很好的選擇。 性能表現則依賴於使用的數據庫,對某些數據庫來講,使用鏈接池並不重要,這個配置就很適合這種情形。UNPOOLED 類型的數據源僅僅須要配置如下 5 種屬性:

  • driver – 這是 JDBC 驅動的 Java 類全限定名(並非 JDBC 驅動中可能包含的數據源類)。
  • url – 這是數據庫的 JDBC URL 地址。
  • username – 登陸數據庫的用戶名。
  • password – 登陸數據庫的密碼。
  • defaultTransactionIsolationLevel – 默認的鏈接事務隔離級別。
  • defaultNetworkTimeout – 等待數據庫操做完成的默認網絡超時時間(單位:毫秒)。查看 java.sql.Connection#setNetworkTimeout() 的 API 文檔以獲取更多信息。

做爲可選項,你也能夠傳遞屬性給數據庫驅動。只需在屬性名加上「driver.」前綴便可,例如:

  • driver.encoding=UTF8

這將經過 DriverManager.getConnection(url, driverProperties) 方法傳遞值爲 UTF8encoding 屬性給數據庫驅動。

POOLED– 這種數據源的實現利用「池」的概念將 JDBC 鏈接對象組織起來,避免了建立新的鏈接實例時所必需的初始化和認證時間。 這種處理方式很流行,能使併發 Web 應用快速響應請求。

除了上述提到 UNPOOLED 下的屬性外,還有更多屬性用來配置 POOLED 的數據源:

  • poolMaximumActiveConnections – 在任意時間可存在的活動(正在使用)鏈接數量,默認值:10
  • poolMaximumIdleConnections – 任意時間可能存在的空閒鏈接數。
  • poolMaximumCheckoutTime – 在被強制返回以前,池中鏈接被檢出(checked out)時間,默認值:20000 毫秒(即 20 秒)
  • poolTimeToWait – 這是一個底層設置,若是獲取鏈接花費了至關長的時間,鏈接池會打印狀態日誌並從新嘗試獲取一個鏈接(避免在誤配置的狀況下一直失敗且不打印日誌),默認值:20000 毫秒(即 20 秒)。
  • poolMaximumLocalBadConnectionTolerance – 這是一個關於壞鏈接容忍度的底層設置, 做用於每個嘗試從緩存池獲取鏈接的線程。 若是這個線程獲取到的是一個壞的鏈接,那麼這個數據源容許這個線程嘗試從新獲取一個新的鏈接,可是這個從新嘗試的次數不該該超過 poolMaximumIdleConnectionspoolMaximumLocalBadConnectionTolerance 之和。 默認值:3(新增於 3.4.5)
  • poolPingQuery – 發送到數據庫的偵測查詢,用來檢驗鏈接是否正常工做並準備接受請求。默認是「NO PING QUERY SET」,這會致使多數數據庫驅動出錯時返回恰當的錯誤消息。
  • poolPingEnabled – 是否啓用偵測查詢。若開啓,須要設置 poolPingQuery 屬性爲一個可執行的 SQL 語句(最好是一個速度很是快的 SQL 語句),默認值:false。
  • poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的頻率。能夠被設置爲和數據庫鏈接超時時間同樣,來避免沒必要要的偵測,默認值:0(即全部鏈接每一時刻都被偵測 — 固然僅當 poolPingEnabled 爲 true 時適用)。

JNDI – 這個數據源實現是爲了能在如 EJB 或應用服務器這類容器中使用,容器能夠集中或在外部配置數據源,而後放置一個 JNDI 上下文的數據源引用。這種數據源配置只須要兩個屬性:

  • initial_context – 這個屬性用來在 InitialContext 中尋找上下文(即,initialContext.lookup(initial_context))。這是個可選屬性,若是忽略,那麼將會直接從 InitialContext 中尋找 data_source 屬性。
  • data_source – 這是引用數據源實例位置的上下文路徑。提供了 initial_context 配置時會在其返回的上下文中進行查找,沒有提供時則直接在 InitialContext 中查找。

JNDI 可理解是一種仿 windows 註冊表形式的數據源。

和其餘數據源配置相似,能夠經過添加前綴「env.」直接把屬性傳遞給 InitialContext。好比:

  • env.encoding=UTF8

這就會在 InitialContext 實例化時往它的構造方法傳遞值爲 UTF8encoding 屬性。

你能夠經過實現接口 org.apache.ibatis.datasource.DataSourceFactory 來使用第三方數據源實現:

public interface DataSourceFactory {
  void setProperties(Properties props);
  DataSource getDataSource();
}

org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory 可被用做父類來構建新的數據源適配器,好比下面這段插入 C3P0 數據源所必需的代碼:

import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {

  public C3P0DataSourceFactory() {
    this.dataSource = new ComboPooledDataSource();
  }
}

爲了令其工做,記得在配置文件中爲每一個但願 MyBatis 調用的 setter 方法增長對應的屬性。 下面是一個能夠鏈接至 PostgreSQL 數據庫的例子:

<dataSource type="org.myproject.C3P0DataSourceFactory">
  <property name="driver" value="org.postgresql.Driver"/>
  <property name="url" value="jdbc:postgresql:mydb"/>
  <property name="username" value="postgres"/>
  <property name="password" value="root"/>
</dataSource>
八、databaseIdProvider(數據庫廠商標識)

MyBatis 能夠根據不一樣的數據庫廠商執行不一樣的語句,這種多廠商的支持是基於映射語句中的 databaseId 屬性。 MyBatis 會加載帶有匹配當前數據庫 databaseId 屬性和全部不帶 databaseId 屬性的語句。 若是同時找到帶有 databaseId 和不帶 databaseId 的相同語句,則後者會被捨棄。 爲支持多廠商特性,只要像下面這樣在 mybatis-config.xml 文件中加入 databaseIdProvider 便可:

<databaseIdProvider type="DB_VENDOR" />

databaseIdProvider 對應的 DB_VENDOR 實現會將 databaseId 設置爲 DatabaseMetaData#getDatabaseProductName() 返回的字符串。 因爲一般狀況下這些字符串都很是長,並且相同產品的不一樣版本會返回不一樣的值,你可能想經過設置屬性別名來使其變短:

<databaseIdProvider type="DB_VENDOR">
  <property name="SQL Server" value="sqlserver"/>
  <property name="DB2" value="db2"/>
  <property name="Oracle" value="oracle" />
</databaseIdProvider>

在提供了屬性別名時,databaseIdProvider 的 DB_VENDOR 實現會將 databaseId 設置爲數據庫產品名與屬性中的名稱第一個相匹配的值,若是沒有匹配的屬性,將會設置爲 「null」。 在這個例子中,若是 getDatabaseProductName() 返回「Oracle (DataDirect)」,databaseId 將被設置爲「oracle」。

你能夠經過實現接口 org.apache.ibatis.mapping.DatabaseIdProvider 並在 mybatis-config.xml 中註冊來構建本身的 DatabaseIdProvider:

public interface DatabaseIdProvider {
  default void setProperties(Properties p) { // 從 3.5.2 開始,該方法爲默認方法
    // 空實現
  }
  String getDatabaseId(DataSource dataSource) throws SQLException;
}
九、mappers(映射器)

既然 MyBatis 的行爲已經由上述元素配置完了,咱們如今就要來定義 SQL 映射語句了。 但首先,咱們須要告訴 MyBatis 到哪裏去找到這些語句。 在自動查找資源方面,Java 並無提供一個很好的解決方案,因此最好的辦法是直接告訴 MyBatis 到哪裏去找映射文件。 你可使用相對於類路徑的資源引用,或徹底限定資源定位符(包括 file:/// 形式的 URL),或類名和包名等。例如:

<!-- 使用相對於類路徑的資源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用徹底限定資源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口實現類的徹底限定類名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 將包內的映射器接口實現所有註冊爲映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解

XML 映射文件

在 XML 核心配置文件介紹中,咱們介紹了映射文件 mapper.xml 的引入。

對於 Mapper 具體的映射配置文件,是 Mybatis 最複雜、最核心的組件,其中的標籤內容體系也是特別詳實,包括它的參數類型、動態SQL、定義SQL、緩存信息等等,咱們在下一篇文章中再進行梳理討論,這裏咱們簡單引出。

Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解

總結

本來我計劃把核心配置文件和映射器 mapper 文件放一塊講,可是發現內容太多太多了,基本核心配置文件就已經講得有點拖堂了,雖然這幾大頂級標籤使用起來已經絕不費力。SQL 映射器配置文件,咱們後續更新,這塊基本是和咱們平常打交道最高頻的操做。

本篇完,本系列下一篇咱們講《 Mybatis系列全解(五):全網最全!詳解Mybatis的Mapper映射文件 》。

Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解
Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解

BIU ~ 文章持續更新,微信搜索「潘潘和他的朋友們」第一時間閱讀,隨時有驚喜。本文會在 GitHub https://github.com/JavaWorld 收錄,熱騰騰的技術、框架、面經、解決方案,咱們都會以最美的姿式第一時間送達,歡迎 Star。

相關文章
相關標籤/搜索