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

封面:洛小汐
作者:潘潘

做大事和做小事的難度是一樣的损肛。兩者都會(huì)消耗你的時(shí)間和精力厢破,所以如果決心做事,就要做大事治拿,要確保你的夢(mèng)想值得追求摩泪,未來的收獲可以配得上你的努力。

前言

上一篇文章 《Mybatis系列全解(三):Mybatis簡(jiǎn)單CRUD使用介紹》 忍啤,我們基本上手了 Mybatis 的增刪改查操作加勤,也感受到 Mybatis 的簡(jiǎn)單高效舒美,但是肯定有部分朋友對(duì)于 Mybatis 的配置文件只是了解基本組成和大致用法同波,尚無(wú)一套完整的結(jié)構(gòu)記憶鳄梅,所以本篇文章我們將詳細(xì)的介紹 Mybatis 的配置全貌,畢竟 Mybatis 的配置文件對(duì)于整個(gè) Mybatis 體系的構(gòu)建與支撐有著深遠(yuǎn)的影響未檩。

Mybatis系列全解腦圖分享戴尸,持續(xù)更新中

Mybaits系列全解 (持續(xù)更新)


目錄


1、為什么要使用配置文件

2悲雳、Mybatis 配置全貌

3挎峦、XML 核心配置

4、XML 映射文件

5合瓢、總結(jié)

為什么要使用配置文件

試想坦胶,如果沒有配置文件,我們的應(yīng)用程序?qū)⒅荒苎刂潭ǖ淖藨B(tài)運(yùn)行晴楔,幾乎不能做任何動(dòng)態(tài)的調(diào)整顿苇,那么這不是一套完美的設(shè)計(jì),因?yàn)槲覀兿M麚碛懈鼘捀`活的操作空間和更多的兼容度税弃,同時(shí)也能解決硬編碼等問題纪岁,所以我們需要有配置文件,對(duì)應(yīng)用程序進(jìn)行參數(shù)預(yù)設(shè)和設(shè)置初始化工作则果。

那我們?yōu)楹午娗閄ML幔翰?

首先漩氨,當(dāng)然是 XML 配置文件本身就足夠優(yōu)秀,格式規(guī)范遗增,存儲(chǔ)小才菠,跨平臺(tái),讀取快...等等贡定,所謂窈窕淑女,誰(shuí)人不愛可都。

其次缓待,也是一個(gè)重要影響因素,就是各大領(lǐng)域大佬的支持渠牲,像微軟旋炒、像Java系...等等,世上本無(wú)路签杈,只是走的人多了瘫镇,也就成了路 ( 這句話是魯迅老先生說的)。

所以答姥,Mybatis選擇搭配XML配置铣除,實(shí)屬合理。

Mybatis 配置全貌

Mybatis框架本身鹦付,理論上就一個(gè)配置文件尚粘,其實(shí)也只需要一個(gè)配置文件,即mybatis-config.xml (當(dāng)然文件名允許自由命名)敲长,只不過這個(gè)配置文件其中的一個(gè)屬性mappers(映射器)郎嫁,由于可能產(chǎn)生過多的SQL映射文件,于是我們物理上單獨(dú)拓展出來祈噪,允許使用者定義任意數(shù)量的 xxxMapper.xml 映射文件泽铛。

把SQL映射文件單獨(dú)配置,是有好處的辑鲤,一是靈活度上允許任意拓展盔腔,二也避免了其它無(wú)需經(jīng)常變動(dòng)的屬性配置遭遇誤改。

我們看看Mybatis官網(wǎng)給出的配置文件層次結(jié)構(gòu):

  • configuration(配置)
    • properties(屬性)
    • settings(設(shè)置)
    • typeAliases(類型別名)
      • 三種別名定義方式
    • typeHandlers(類型處理器)
      • 自定義類型處理器
    • objectFactory(對(duì)象工廠)
    • plugins(插件)
    • environments(環(huán)境配置)
      • environment(環(huán)境變量)
        • transactionManager(事務(wù)管理器)
        • dataSource(數(shù)據(jù)源)
          • 三種支持?jǐn)?shù)據(jù)源與自定義數(shù)據(jù)源
    • databaseIdProvider(數(shù)據(jù)庫(kù)廠商標(biāo)識(shí))
    • mappers(映射器)

實(shí)際配置文件XML內(nèi)容如下遂填,除了約束頭 <?xml> 與 <!DOCTYPE>铲觉,

其余標(biāo)簽元素都是 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>

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

    <!-- 2吓坚、設(shè)置:定義全局性設(shè)置撵幽,例如開啟二級(jí)緩存 -->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

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

    <!-- 4礁击、類型處理器:定義Java類型與數(shù)據(jù)庫(kù)中的數(shù)據(jù)類型之間的轉(zhuǎn)換關(guān)系 -->
    <typeHandlers></typeHandlers>

    <!-- 5盐杂、對(duì)象工廠 -->
    <objectFactory type=""></objectFactory>

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

    <!-- 7链烈、環(huán)境:配置mybatis的環(huán)境 -->
    <environments default="development">
        <!-- 環(huán)境變量:支持多套環(huán)境變量厉斟,例如開發(fā)環(huán)境、生產(chǎn)環(huán)境 -->
        <environment id="development">
            <!-- 事務(wù)管理器:默認(rèn)JDBC -->
            <transactionManager type="JDBC" />
            <!-- 數(shù)據(jù)源:使用連接池强衡,并加載mysql驅(qū)動(dòng)連接數(shù)據(jù)庫(kù) -->
            <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>

    <!-- 8擦秽、數(shù)據(jù)庫(kù)廠商標(biāo)識(shí) -->
    <databaseIdProvider type=""></databaseIdProvider>

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

必須注意:Mybatis配置文件的屬性位置順序是 固定 的漩勤,不允許 顛倒順序感挥,否則 Mybatis 在解析 XML 文件的時(shí)候就會(huì)拋出異常,這個(gè)與 Mybatis 框架啟動(dòng)加載配置信息順序有關(guān)越败,后續(xù)我們?cè)创a分析會(huì)講到触幼。

以上基本能夠清晰看明白 Mybatis 配置文件的層次結(jié)構(gòu)關(guān)系,我們簡(jiǎn)單畫一張腦圖:

基本是需要我們掌握 9 大頂級(jí)元素配置究飞,其中標(biāo)記 橘紅色 的屬性配置置谦,由于涉及 插件動(dòng)態(tài)SQL ,插件配置可以應(yīng)用于分頁(yè)與功能增強(qiáng)等亿傅,動(dòng)態(tài)SQL例如 if 標(biāo)簽媒峡、where 標(biāo)簽、foreach標(biāo)簽等葵擎,初步理解為應(yīng)用于SQL語(yǔ)句拼接丝蹭。這兩塊屬于 Mybatis 的兩個(gè)特性,我們后續(xù)單獨(dú)詳細(xì)進(jìn)行梳理討論坪蚁。

XML 核心配置

我們的核心配置文件 configuration(配置)作為最頂級(jí)節(jié)點(diǎn)奔穿,其余 9 大屬性都必須嵌套在其內(nèi),對(duì)于內(nèi)部 9 大節(jié)點(diǎn)敏晤,我們逐一講解:

1贱田、properties(屬性)

屬性標(biāo)簽,顯而易見就是提供屬性配置嘴脾,可進(jìn)行動(dòng)態(tài)替換男摧,一般可以在 Java 屬性文件中配置,例如 jdbc.properties 配置文件 译打,或通過 properties 元素標(biāo)簽中的子元素 property 來指定配置耗拓。

舉例我們需要配置數(shù)據(jù)源信息,采用 property 標(biāo)簽可以這樣配置:


<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>

設(shè)置好的屬性可以在整個(gè)配置文件中用來替換需要?jiǎng)討B(tài)配置的屬性值奏司。比如:


<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 中的屬性配置文件乔询,把屬性配置元素具體化到一個(gè)屬性文件中,并且使用屬性文件的 key 名作為占位符韵洋。例如 jdbc.properties


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

使用時(shí)我們把屬性文件引入竿刁,并使用文件中定義的占位符黄锤,例如 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>

但是問題來了,當(dāng)我們既使用 *.properties 配置文件食拜,同時(shí)又設(shè)置了 property 元素值鸵熟,Mybatis 會(huì)使用哪邊配置的屬性值呢? 例如這種情況 :

<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 標(biāo)簽元素與 jdbc.properties 文件中同時(shí)存在相同屬性流强,那么屬性文件將會(huì)覆蓋 property 標(biāo)簽元素的屬性,例如最終 username屬性值會(huì)使用 jdbc.properties 文件中設(shè)置的 root呻待,而不會(huì)使用屬性元素設(shè)置的 user1 煮盼。這樣實(shí)際為配置提供了諸多靈活選擇。

另外带污,properties 元素允許配置 resource 屬性或 url 屬性,只能二選一香到,要么使用 resource 指定本地的配置文件鱼冀,要么使用 url 指定遠(yuǎn)程的配置文件,因?yàn)?Mybatis 在加載配置時(shí)悠就,如果發(fā)現(xiàn) url 與 resource 同時(shí)存在千绪,會(huì)拋出異常禁止。


<!-- 配置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 在解析配置的時(shí)候荸型,也可以在 Java 代碼中構(gòu)建屬性 java.util.Properties 屬性對(duì)象并傳遞到 SqlSessionFactoryBuilder.build() 方法中,例如:

// 構(gòu)建屬性對(duì)象
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");  

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

那么這三種方式都允許配置炸茧,那在屬性配置重復(fù)的情況下瑞妇,優(yōu)先級(jí)別是怎樣呢?

properties 優(yōu)先級(jí)

1梭冠、第一優(yōu)先級(jí):在 Java 代碼中構(gòu)建的 properties 屬性對(duì)象辕狰;

2、第二優(yōu)先級(jí):通過屬性 resource 或 url 讀取到的本地文件或遠(yuǎn)程文件控漠;

3蔓倍、第三優(yōu)先級(jí):直接在 properties 內(nèi)部子標(biāo)簽元素 property 中設(shè)置的屬性。

注意盐捷,在實(shí)際開發(fā)中偶翅,為了避免給后期維護(hù)造成困擾,建議使用單一種配置方式碉渡。

2聚谁、settings(設(shè)置)

settings 標(biāo)簽元素,是 MyBatis 中極為重要的調(diào)整設(shè)置滞诺,它們會(huì)動(dòng)態(tài)改變 MyBatis 的運(yùn)行時(shí)行為垦巴,這些配置就像 Mybatis 內(nèi)置的許多功能媳搪,當(dāng)你需要使用時(shí)可以根據(jù)需要靈活調(diào)整,并且 settings 能配置的東西特別多骤宣,我們先來一起看看秦爆,一個(gè)完整的屬性配置示例:


<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

    • 全局性地開啟或關(guān)閉所有映射器配置文件中已配置的任何緩存
    • 支持 true | false
    • 默認(rèn) true
  • 屬性lazyLoadingEnabled

    • 延遲加載的全局開關(guān)。當(dāng)開啟時(shí)憔披,所有關(guān)聯(lián)對(duì)象都會(huì)延遲加載等限。 特定關(guān)聯(lián)關(guān)系中可通過設(shè)置 fetchType 屬性來覆蓋該項(xiàng)的開關(guān)狀態(tài)。
    • 支持 true | false
    • 默認(rèn) false
  • 屬性 aggressiveLazyLoading

    • 開啟時(shí)芬膝,任一方法的調(diào)用都會(huì)加載該對(duì)象的所有延遲加載屬性望门。 否則,每個(gè)延遲加載屬性會(huì)按需加載(參考 lazyLoadTriggerMethods)锰霜。
    • 支持 true | false
    • 默認(rèn) false (在 3.4.1 及之前的版本中默認(rèn)為 true)
  • 屬性 multipleResultSetsEnabled

    • 是否允許單個(gè)語(yǔ)句返回多結(jié)果集(需要數(shù)據(jù)庫(kù)驅(qū)動(dòng)支持)筹误。
    • 支持 true | false
    • 默認(rèn) true
  • 屬性 useColumnLabel

    • 使用列標(biāo)簽代替列名。實(shí)際表現(xiàn)依賴于數(shù)據(jù)庫(kù)驅(qū)動(dòng)癣缅,具體可參考數(shù)據(jù)庫(kù)驅(qū)動(dòng)的相關(guān)文檔厨剪,或通過對(duì)比測(cè)試來觀察。
    • 支持 true | false
    • 默認(rèn) true
  • 屬性 useGeneratedKeys

    • 允許 JDBC 支持自動(dòng)生成主鍵友存,需要數(shù)據(jù)庫(kù)驅(qū)動(dòng)支持祷膳。如果設(shè)置為 true,將強(qiáng)制使用自動(dòng)生成主鍵屡立。盡管一些數(shù)據(jù)庫(kù)驅(qū)動(dòng)不支持此特性直晨,但仍可正常工作(如 Derby)。
    • 支持 true | false
    • 默認(rèn) false
  • 屬性 autoMappingBehavior

    • 指定 MyBatis 應(yīng)如何自動(dòng)映射列到字段或?qū)傩浴?NONE 表示關(guān)閉自動(dòng)映射膨俐;PARTIAL 只會(huì)自動(dòng)映射沒有定義嵌套結(jié)果映射的字段勇皇。 FULL 會(huì)自動(dòng)映射任何復(fù)雜的結(jié)果集(無(wú)論是否嵌套)。
    • 支持 NONE, PARTIAL, FULL
    • 默認(rèn) PARTIAL
  • 屬性 autoMappingUnknownColumnBehavior

    • 指定發(fā)現(xiàn)自動(dòng)映射目標(biāo)未知列(或未知屬性類型)的行為焚刺。
      • NONE: 不做任何反應(yīng)
      • WARNING: 輸出警告日志( org.apache.ibatis.session.AutoMappingUnknownColumnBehavior 的日志等級(jí)必須設(shè)置為 WARN)
      • FAILING: 映射失敗 (拋出 SqlSessionException)
    • 支持 NONE, WARNING, FAILING
    • 默認(rèn) NONE
  • 屬性 defaultExecutorType

    • 配置默認(rèn)的執(zhí)行器儒士。SIMPLE 就是普通的執(zhí)行器;REUSE 執(zhí)行器會(huì)重用預(yù)處理語(yǔ)句(PreparedStatement)檩坚; BATCH 執(zhí)行器不僅重用語(yǔ)句還會(huì)執(zhí)行批量更新着撩。
    • 支持 SIMPLE REUSE BATCH
    • 默認(rèn) SIMPLE
  • 屬性 defaultStatementTimeout

    • 設(shè)置超時(shí)時(shí)間,它決定數(shù)據(jù)庫(kù)驅(qū)動(dòng)等待數(shù)據(jù)庫(kù)響應(yīng)的秒數(shù)匾委。
    • 支持 任意正整數(shù)
    • 默認(rèn) 未設(shè)置 (null)
  • 屬性 defaultFetchSize

    • 動(dòng)的結(jié)果集獲取數(shù)量(fetchSize)設(shè)置一個(gè)建議值拖叙。此參數(shù)只可以在查詢?cè)O(shè)置中被覆蓋。
    • 支持 任意正整數(shù)
    • 默認(rèn) 未設(shè)置 (null)
  • 屬性 defaultResultSetType

    • 指定語(yǔ)句默認(rèn)的滾動(dòng)策略赂乐。(新增于 3.5.2)
    • 支持 FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未設(shè)置)
    • 默認(rèn) 未設(shè)置 (null)
  • 屬性 safeRowBoundsEnabled

    • 是否允許在嵌套語(yǔ)句中使用分頁(yè)(RowBounds)薯鳍。如果允許使用則設(shè)置為 false。
    • 支持 true | false
    • 默認(rèn) false
  • 屬性 safeResultHandlerEnabled

    • 是否允許在嵌套語(yǔ)句中使用結(jié)果處理器(ResultHandler)。如果允許使用則設(shè)置為 false挖滤。
    • 支持 true | false
    • 默認(rèn) true
  • 屬性 mapUnderscoreToCamelCase

    • 是否開啟駝峰命名自動(dòng)映射崩溪,即從經(jīng)典數(shù)據(jù)庫(kù)列名 A_COLUMN 映射到經(jīng)典 Java 屬性名 aColumn。
    • 支持 true | false
    • 默認(rèn) false
  • 屬性 localCacheScope

    • MyBatis 利用本地緩存機(jī)制(Local Cache)防止循環(huán)引用和加速重復(fù)的嵌套查詢斩松。 默認(rèn)值為 SESSION伶唯,會(huì)緩存一個(gè)會(huì)話中執(zhí)行的所有查詢。 若設(shè)置值為 STATEMENT惧盹,本地緩存將僅用于執(zhí)行語(yǔ)句乳幸,對(duì)相同 SqlSession 的不同查詢將不會(huì)進(jìn)行緩存。
    • 支持 SESSION | STATEMENT
    • 默認(rèn) SESSION
  • 屬性 jdbcTypeForNull

    • 當(dāng)沒有為參數(shù)指定特定的 JDBC 類型時(shí)钧椰,空值的默認(rèn) JDBC 類型粹断。 某些數(shù)據(jù)庫(kù)驅(qū)動(dòng)需要指定列的 JDBC 類型,多數(shù)情況直接用一般類型即可嫡霞,比如 NULL瓶埋、VARCHAR 或 OTHER。
    • JdbcType 常量诊沪,常用值:NULL养筒、VARCHAR 或 OTHER。
    • 默認(rèn) OTHER
  • 屬性 lazyLoadTriggerMethods

    • 指定對(duì)象的哪些方法觸發(fā)一次延遲加載娄徊。
    • 支持 用逗號(hào)分隔的方法列表。
    • 默認(rèn) equals,clone,hashCode,toString
  • 屬性 defaultScriptingLanguage

    • 指定動(dòng)態(tài) SQL 生成使用的默認(rèn)腳本語(yǔ)言盾戴。
    • 支持 一個(gè)類型別名或全限定類名寄锐。
    • 默認(rèn) org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
  • 屬性 defaultEnumTypeHandler

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

    • 指定當(dāng)結(jié)果集中值為 null 的時(shí)候是否調(diào)用映射對(duì)象的 setter(map 對(duì)象時(shí)為 put)方法橄仆,這在依賴于 Map.keySet() 或 null 值進(jìn)行初始化時(shí)比較有用。注意基本類型(int衅斩、boolean 等)是不能設(shè)置成 null 的盆顾。
    • 支持 true | false
    • 默認(rèn) false
  • 屬性 returnInstanceForEmptyRow

    • 當(dāng)返回行的所有列都是空時(shí),MyBatis默認(rèn)返回 null畏梆。 當(dāng)開啟這個(gè)設(shè)置時(shí)您宪,MyBatis會(huì)返回一個(gè)空實(shí)例。 請(qǐng)注意奠涌,它也適用于嵌套的結(jié)果集(如集合或關(guān)聯(lián))宪巨。(新增于 3.4.2)
    • 支持 true | false
    • 默認(rèn) false
  • 屬性 logPrefix

    • 指定 MyBatis 增加到日志名稱的前綴。
    • 支持 任何字符串
    • 默認(rèn) 未設(shè)置
  • 屬性 logImpl

    • 指定 MyBatis 所用日志的具體實(shí)現(xiàn)溜畅,未指定時(shí)將自動(dòng)查找捏卓。
    • 支持 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
    • 默認(rèn) 未設(shè)置
  • 屬性 proxyFactory

    • 指定 Mybatis 創(chuàng)建可延遲加載對(duì)象所用到的代理工具。
    • 支持 CGLIB | JAVASSIST
    • 默認(rèn) JAVASSIST (MyBatis 3.3 以上
  • 屬性 vfsImpl

    • 指定 VFS 的實(shí)現(xiàn)
    • 支持 自定義 VFS 的實(shí)現(xiàn)的類全限定名慈格,以逗號(hào)分隔怠晴。
    • 默認(rèn) 未設(shè)置
  • 屬性 useActualParamName

    • 允許使用方法簽名中的名稱作為語(yǔ)句參數(shù)名稱遥金。 為了使用該特性,你的項(xiàng)目必須采用 Java 8 編譯蒜田,并且加上 -parameters 選項(xiàng)稿械。(新增于 3.4.1)
    • 支持 true | false
    • 默認(rèn) true
  • 屬性 configurationFactory

    • 指定一個(gè)提供 Configuration 實(shí)例的類。 這個(gè)被返回的 Configuration 實(shí)例用來加載被反序列化對(duì)象的延遲加載屬性值物邑。 這個(gè)類必須包含一個(gè)簽名為static Configuration getConfiguration() 的方法溜哮。(新增于 3.2.3)
    • 支持 一個(gè)類型別名或完全限定類名。
    • 默認(rèn) 未設(shè)置
  • 屬性 shrinkWhitespacesInSql

    • 從SQL中刪除多余的空格字符色解。請(qǐng)注意茂嗓,這也會(huì)影響SQL中的文字字符串。 (新增于 3.5.5)
    • 支持 true | false
    • 默認(rèn) false
  • 屬性 defaultSqlProviderType

    • 指定一個(gè)本身?yè)聿樵兎椒ǖ念悾?從 3.5.6 開始 )科阎,這個(gè)類可以配置在注解 @SelectProvider 的 type 屬性值上述吸。
    • 支持 一個(gè)類型別名或完全限定類名。
    • 默認(rèn) 未設(shè)置

settings 支持了特別多功能支持锣笨,其實(shí)常規(guī)開發(fā)中使用到的屬性項(xiàng)不會(huì)特別多蝌矛,除非項(xiàng)目有特殊要求,所以建議大家把這些設(shè)置當(dāng)做字典即可错英,不必詳記 每一個(gè)屬性使用入撒,需要時(shí)翻閱研讀。

3椭岩、typeAliases(類型別名)

類型別名可以給 Java 類型設(shè)置一個(gè)簡(jiǎn)稱茅逮。 它僅用于 XML 配置,意在降低冗余的全限定類名書寫判哥,因?yàn)闀鴮戭惖娜薅L(zhǎng)了献雅,我們希望有一個(gè)簡(jiǎn)稱來指代它。類型別名在 Mybatis 中分為 系統(tǒng)內(nèi)置用戶自定義 兩類塌计,Mybatis 會(huì)在解析配置文件時(shí)把 typeAliases 實(shí)例存儲(chǔ)進(jìn)入 Configuration 對(duì)象中挺身,需要使用時(shí)直接獲取。

一般我們可以自定義別名锌仅,例如:

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

像這樣配置時(shí)章钾,我們就可以在任何需要使用 domain.blog.Author 的地方,直接使用別名 author 热芹。

但是伍玖,如果遇到項(xiàng)目中特別多 Java 類需要配置別名,怎么更快的設(shè)置呢?

可以指定一個(gè)包名進(jìn)行掃描,MyBatis 會(huì)在包名下面掃描需要的 Java Bean值桩,比如:

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

每一個(gè)在包 domain.blog 中的 Java Bean挺物,在沒有注解的情況下椰棘,會(huì)使用 Bean 的首字母小寫的非限定類名來作為它的別名纺棺。 比如 domain.blog.Author 的別名為 author;若有 注解 邪狞,則別名為其自定義的注解值祷蝌。見下面的例子:

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

Mybatis 已經(jīng)為許多常見的 Java 類型內(nèi)建了相應(yīng)的類型別名。下面就是一些為常見的 Java 類型內(nèi)建的類型別名帆卓。它們都是不區(qū)分大小寫的巨朦,注意,為了應(yīng)對(duì)原始類型的命名重復(fù)剑令,采取了特殊的命名風(fēng)格糊啡,可以發(fā)現(xiàn) 基本類型 的別名前綴都有下劃線 ‘_’,而基本類型的 包裝類 則沒有吁津,這個(gè)需要注意:

  • 別名 _byte棚蓄,對(duì)應(yīng)的類型是:byte
  • 別名 _long,對(duì)應(yīng)的類型是:long
  • 別名 _short碍脏,對(duì)應(yīng)的類型是:short
  • 別名 _int梭依,對(duì)應(yīng)的類型是:int
  • 別名 _integer,對(duì)應(yīng)的類型是:int
  • 別名 _double典尾,對(duì)應(yīng)的類型是:double
  • 別名 _float役拴,對(duì)應(yīng)的類型是:float
  • 別名 _boolean,對(duì)應(yīng)的類型是:boolean
  • 別名 string钾埂,對(duì)應(yīng)的類型是:String
  • 別名 byte河闰,對(duì)應(yīng)的類型是:Byte
  • 別名 long,對(duì)應(yīng)的類型是:Long
  • 別名 short勃教,對(duì)應(yīng)的類型是:Short
  • 別名 int淤击,對(duì)應(yīng)的類型是:Integer
  • 別名 integer匠抗,對(duì)應(yīng)的類型是:Integer
  • 別名 double故源,對(duì)應(yīng)的類型是:Double
  • 別名 float,對(duì)應(yīng)的類型是:Float
  • 別名 boolean汞贸,對(duì)應(yīng)的類型是:Boolean
  • 別名 date绳军,對(duì)應(yīng)的類型是:Date
  • 別名 decimal,對(duì)應(yīng)的類型是:BigDecimal
  • 別名 bigdecimal矢腻,對(duì)應(yīng)的類型是:BigDecimal
  • 別名 object门驾,對(duì)應(yīng)的類型是:Object
  • 別名 map,對(duì)應(yīng)的類型是:Map
  • 別名 hashmap多柑,對(duì)應(yīng)的類型是:HashMap
  • 別名 list奶是,對(duì)應(yīng)的類型是:List
  • 別名 arraylist,對(duì)應(yīng)的類型是:ArrayList
  • 別名 collection,對(duì)應(yīng)的類型是:Collection
  • 別名 iterator聂沙,對(duì)應(yīng)的類型是:Iterator

我們可以通過源碼查看內(nèi)置的類型別名的注冊(cè)信息秆麸。

具體源碼路徑在 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);
  }

別名是不區(qū)分大小寫的,同時(shí)也支持?jǐn)?shù)組類型及汉,只需要加 “[]” 即可使用沮趣,比如 Long 數(shù)組別名我們可以用 long[] 直接代替,例如在實(shí)際開發(fā)中坷随,int 房铭、INT 、integer 温眉、INTEGER 都是代表 Integer 缸匪, 這里主要由于 MyBatis 在注冊(cè)別名的時(shí)候會(huì)全部轉(zhuǎn)為小寫字母進(jìn)行存儲(chǔ),另外以上列表 無(wú)需牢記芍殖,僅僅在需要使用的時(shí)候查閱即可豪嗽,基本也都可以看得明白。

4豌骏、typeHandlers(類型處理器)

MyBatis 在設(shè)置預(yù)處理SQL語(yǔ)句(PreparedStatement)中所需要的 參數(shù) 或從 結(jié)果集 ResultSet 中獲取對(duì)象時(shí)龟梦, 都會(huì)用類型處理器將獲取到的值以合適的方式轉(zhuǎn)換成 Java 類型。

類型處理器窃躲,主要用于處理 Java 類型與 JDBC 類型的映射匹配關(guān)系處理计贰,下表描述了一些默認(rèn)的類型處理器。

  • 類型處理器 BooleanTypeHandler
    • Java 類型:java.lang.Boolean, boolean
    • JDBC 類型:數(shù)據(jù)庫(kù)兼容的 BOOLEAN
  • 類型處理器 ByteTypeHandler
    • Java 類型:java.lang.Byte, byte
    • JDBC 類型:數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 BYTE
  • 類型處理器 ShortTypeHandler
    • Java 類型:java.lang.Short, short
    • JDBC 類型:數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 SMALLINT
  • 類型處理器 IntegerTypeHandler
    • Java 類型:java.lang.Integer, int
    • JDBC 類型:數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 INTEGER
  • 類型處理器 LongTypeHandler
    • Java 類型:java.lang.Long, long
    • JDBC 類型:數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 BIGINT
  • 類型處理器 FloatTypeHandler
    • Java 類型:java.lang.Float, float
    • JDBC 類型:數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 FLOAT
  • 類型處理器 DoubleTypeHandler
    • Java 類型:java.lang.Double, double
    • JDBC 類型:數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 DOUBLE
  • 類型處理器 BigDecimalTypeHandler
    • Java 類型:java.math.BigDecimal
    • JDBC 類型:數(shù)據(jù)庫(kù)兼容的 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 類型:數(shù)據(jù)庫(kù)兼容的字節(jié)流類型
  • 類型處理器 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 或任何兼容的字符串類型蒂窒,用來存儲(chǔ)枚舉的名稱(而不是索引序數(shù)值)
  • 類型處理器 EnumOrdinalTypeHandler
    • Java 類型:Enumeration Type
    • JDBC 類型:任何兼容的 NUMERIC 或 DOUBLE 類型躁倒,用來存儲(chǔ)枚舉的序數(shù)值(而不是名稱)。
  • 類型處理器 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

我們可以通過源碼查看內(nèi)置的類型別名的注冊(cè)信息洒琢。

具體源碼路徑在 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 默認(rèn)支持 JSR-310(日期和時(shí)間 API) ,可以在以上源碼上看到新增支持。

一般衰抑,你可以重寫已有的類型處理器象迎,

或根據(jù)業(yè)務(wù)需要?jiǎng)?chuàng)建你自己的類型處理器,

以處理不支持的類型或非標(biāo)準(zhǔn)的類型呛踊。

具體做法為:

1砾淌、實(shí)現(xiàn) org.apache.ibatis.type.TypeHandler 接口;

2谭网、繼承 org.apache.ibatis.type.BaseTypeHandler 類汪厨。

本身 BaseTypeHandler 類作為抽象類就已經(jīng)實(shí)現(xiàn)了 TypeHandler 接口。

所以我們看到接口 TypeHandler 定義了四個(gè)方法:

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 我們就可以知道愉择,是發(fā)生在預(yù)編譯時(shí)設(shè)置參數(shù)(增刪改查傳入?yún)?shù))與查詢結(jié)果集后轉(zhuǎn)換為 Java 類型時(shí)劫乱,類型處理器發(fā)揮作用织中。

具體實(shí)現(xiàn)如下,先自定義類型處理器類 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);
  }
}

自定義類已設(shè)定:JdbcType.VARCHAR 與 String 類做映射轉(zhuǎn)換(注解和泛型已體現(xiàn))衷戈。

其次抠璃,在核心配置文件中設(shè)置類型處理器:

<!-- 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 的配置方式優(yōu)先級(jí)高于注解配置方式。

這里拉一,自定義類型處理器將會(huì)覆蓋已有的處理 Java String 類型的屬性以及 VARCHAR 類型的參數(shù)和結(jié)果的類型處理器采盒,基本以上步驟就已經(jīng)自定了 JdbcType.VARCHARString類做映射轉(zhuǎn)換。

其實(shí)到這里蔚润,我們基本也就完成了類型處理器的自定義轉(zhuǎn)換磅氨,但是有一種情況,就是我們希望我們自定義的類型處理器只處理某一個(gè) Java 實(shí)體中的 JdbcType.VARCHAR 與 String 類映射轉(zhuǎn)換嫡纠,其它實(shí)體的處理還是使用系統(tǒng)內(nèi)置的轉(zhuǎn)換烦租,很簡(jiǎn)單,我們只需要把以上兩步都去掉除盏,在自定義類型處理類的注解@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 實(shí)體參考:

package com.panshenlian.pojo;

/**
 * @Author: panshenlian
 * @Description: 用戶實(shí)體
 * @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;
    } 
}

最終自定義類型處理器待榔,只會(huì)對(duì) birthday 字段產(chǎn)生影響逞壁,其余字段均不受影響。

自定義類型處理器很靈活究抓,只有當(dāng)指定對(duì)應(yīng)的 Java 類型和 Jdbc 類型時(shí)猾担,處理器才會(huì)具體生效袭灯,否則 Mybatis 會(huì)默認(rèn)匹配系統(tǒng)內(nèi)置的類型處理器刺下。

另外,當(dāng)我們自定義很多類型處理器時(shí)稽荧,系統(tǒng)支持配置包掃描的方式查找類型處理器:

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

注意在使用自動(dòng)發(fā)現(xiàn)功能的時(shí)候橘茉,只能通過注解方式來指定 JDBC 的類型工腋。

你可以創(chuàng)建能夠處理多個(gè)類的泛型類型處理器。為了使用泛型類型處理器畅卓, 需要增加一個(gè)接受該類的 class 作為參數(shù)的構(gòu)造器擅腰,這樣 MyBatis 會(huì)在構(gòu)造一個(gè)類型處理器實(shí)例的時(shí)候傳入一個(gè)具體的類。

//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 中選擇一個(gè)來使用趁冈。

比如說我們想存儲(chǔ)取近似值時(shí)用到的舍入模式。默認(rèn)情況下拜马,MyBatis 會(huì)利用 EnumTypeHandler 來把 Enum 值轉(zhuǎn)換成對(duì)應(yīng)的名字渗勘。

注意 EnumTypeHandler 在某種意義上來說是比較特別的,其它的處理器只針對(duì)某個(gè)特定的類俩莽,而它不同旺坠,它會(huì)處理任意繼承了 Enum 的類。

不過扮超,我們可能不想存儲(chǔ)名字取刃,相反我們的 DBA 會(huì)堅(jiān)持使用整形值代碼。那也一樣簡(jiǎn)單:在配置文件中把 EnumOrdinalTypeHandler 加到 typeHandlers 中即可出刷, 這樣每個(gè) RoundingMode 將通過他們的序數(shù)值來映射成對(duì)應(yīng)的整形數(shù)值璧疗。

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

但要是你想在一個(gè)地方將 Enum 映射成字符串,在另外一個(gè)地方映射成整形值呢馁龟?

自動(dòng)映射器(auto-mapper)會(huì)自動(dòng)地選用 EnumOrdinalTypeHandler 來處理枚舉類型病毡, 所以如果我們想用普通的 EnumTypeHandler,就必須要顯式地為那些 SQL 語(yǔ)句設(shè)置要使用的類型處理器屁柏。

下一篇文章我們才開始介紹映射器 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 語(yǔ)句強(qiáng)制使用 resultMap 來代替 resultType裸删。

5八拱、objectFactory(對(duì)象工廠)

每次 MyBatis 創(chuàng)建結(jié)果對(duì)象的新實(shí)例時(shí),它都會(huì)使用一個(gè)對(duì)象工廠(ObjectFactory)實(shí)例來完成實(shí)例化工作涯塔。 默認(rèn)的對(duì)象工廠需要做的僅僅是實(shí)例化目標(biāo)類肌稻,要么通過默認(rèn)無(wú)參構(gòu)造方法,要么通過存在的參數(shù)映射來調(diào)用帶有參數(shù)的構(gòu)造方法匕荸。 如果想覆蓋對(duì)象工廠的默認(rèn)行為爹谭,可以通過創(chuàng)建自己的對(duì)象工廠來實(shí)現(xiàn)。比如:

// 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 接口很簡(jiǎn)單榛搔,它包含兩個(gè)創(chuàng)建用的方法诺凡,一個(gè)是處理默認(rèn)構(gòu)造方法的东揣,另外一個(gè)是處理帶參數(shù)的構(gòu)造方法的。 最后腹泌,setProperties 方法可以被用來配置 ObjectFactory嘶卧,在初始化你的 ObjectFactory 實(shí)例后, objectFactory 元素體中定義的屬性會(huì)被傳遞給 setProperties 方法凉袱。

正常情況下我們不需要使用到芥吟,或者說不建議使用,除非業(yè)務(wù)上確實(shí)需要對(duì)一個(gè)特殊實(shí)體初始構(gòu)造做一個(gè)默認(rèn)屬性值配置等處理专甩,其余情況不推薦使用运沦,避免產(chǎn)生不可控風(fēng)險(xiǎn)。

6配深、plugins(插件)

MyBatis 允許你在映射語(yǔ)句執(zhí)行過程中的某一點(diǎn)進(jìn)行攔截調(diào)用携添。默認(rèn)情況下,MyBatis 允許使用插件來攔截的方法調(diào)用包括:

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

插件功能主要開放攔截的對(duì)象就是以上列舉的 Mybatis 四大組件篓叶,后續(xù)我們講 Mybatis 核心API 的時(shí)候或者單獨(dú)介紹自定義插件的時(shí)候會(huì)詳細(xì)說明烈掠,這里大家可以先大致了解,包括數(shù)據(jù)分頁(yè)缸托、操作日志增強(qiáng)左敌、sql 性能監(jiān)控等都可以通過插件實(shí)現(xiàn),不過會(huì)存儲(chǔ)改造的風(fēng)險(xiǎn)俐镐,畢竟這些都是核心的 API 矫限。

這四大類中方法具體可以通過查看每個(gè)方法的簽名來發(fā)現(xiàn),或者直接查看 MyBatis 發(fā)行包中的源代碼佩抹。 如果你想做的不僅僅是監(jiān)控方法的調(diào)用叼风,那么你最好相當(dāng)了解要重寫的方法的行為。 因?yàn)樵谠噲D修改或重寫已有方法的行為時(shí)棍苹,很可能會(huì)破壞 MyBatis 的核心模塊无宿。 這些都是更底層的類和方法,所以使用插件的時(shí)候要特別當(dāng)心枢里。

通過 MyBatis 提供的強(qiáng)大機(jī)制孽鸡,使用插件是非常簡(jiǎn)單的,只需實(shí)現(xiàn) Interceptor 接口栏豺,并指定想要攔截的類彬碱,方法,參數(shù)(由于有多態(tài)的情況)即可奥洼。

// 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>

上面的插件將會(huì)攔截在 Executor 實(shí)例中所有的 “update” 方法調(diào)用巷疼, 這里的 Executor 是負(fù)責(zé)執(zhí)行底層映射語(yǔ)句的內(nèi)部對(duì)象。

覆蓋配置類 「 謹(jǐn)慎使用溉卓,存在風(fēng)險(xiǎn) 」

除了用插件來修改 MyBatis 核心行為以外皮迟,還可以通過完全覆蓋配置類來達(dá)到目的。只需繼承配置類后覆蓋其中的某個(gè)方法桑寨,再把它傳遞到 SqlSessionFactoryBuilder.build(myConfig) 方法即可伏尼。再次重申,這可能會(huì)極大影響 MyBatis 的行為尉尾,務(wù)請(qǐng)慎之又慎爆阶。

7、environments(環(huán)境配置)

MyBatis 可以配置成適應(yīng)多種環(huán)境沙咏,這種機(jī)制有助于將 SQL 映射應(yīng)用于多種數(shù)據(jù)庫(kù)之中辨图, 現(xiàn)實(shí)情況下有多種理由需要這么做。例如肢藐,開發(fā)故河、測(cè)試和生產(chǎn)環(huán)境需要有不同的配置;或者想在具有相同 Schema 的多個(gè)生產(chǎn)數(shù)據(jù)庫(kù)中使用相同的 SQL 映射吆豹。還有許多類似的使用場(chǎng)景鱼的。

不過要記住:盡管可以配置多個(gè)環(huán)境痘煤,但每個(gè) SqlSessionFactory 實(shí)例只能選擇一種環(huán)境凑阶。

所以,如果你想連接兩個(gè)數(shù)據(jù)庫(kù)衷快,就需要?jiǎng)?chuàng)建兩個(gè) SqlSessionFactory 實(shí)例宙橱,每個(gè)數(shù)據(jù)庫(kù)對(duì)應(yīng)一個(gè)。而如果是三個(gè)數(shù)據(jù)庫(kù)蘸拔,就需要三個(gè)實(shí)例师郑,依此類推,記起來很簡(jiǎn)單:

每個(gè)數(shù)據(jù)庫(kù)對(duì)應(yīng)一個(gè) SqlSessionFactory 實(shí)例调窍。

為了指定創(chuàng)建哪種環(huán)境呕乎,只要將它作為可選的參數(shù)傳遞給 SqlSessionFactoryBuilder 即可≡删В可以接受環(huán)境配置的兩個(gè)方法簽名是:

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

如果忽略了環(huán)境參數(shù)猬仁,那么將會(huì)加載默認(rèn)環(huán)境,如下所示:

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

environments 元素定義了如何配置環(huán)境先誉。

<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>

注意一些關(guān)鍵點(diǎn):

  • 默認(rèn)使用的環(huán)境 ID(比如:default="development")湿刽。
  • 每個(gè) environment 元素定義的環(huán)境 ID(比如:id="development")。
  • 事務(wù)管理器的配置(比如:type="JDBC")褐耳。
  • 數(shù)據(jù)源的配置(比如:type="POOLED")诈闺。

默認(rèn)環(huán)境和環(huán)境 ID 顧名思義。 環(huán)境可以隨意命名铃芦,但務(wù)必保證默認(rèn)的環(huán)境 ID 要匹配其中一個(gè)環(huán)境 ID雅镊。

事務(wù)管理器(transactionManager)

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

  • JDBC – 這個(gè)配置直接使用了 JDBC 的提交和回滾設(shè)施襟雷,它依賴從數(shù)據(jù)源獲得的連接來管理事務(wù)作用域。
  • MANAGED – 這個(gè)配置幾乎沒做什么仁烹。它從不提交或回滾一個(gè)連接耸弄,而是讓容器來管理事務(wù)的整個(gè)生命周期(比如 JEE 應(yīng)用服務(wù)器的上下文)。 默認(rèn)情況下它會(huì)關(guān)閉連接卓缰。然而一些容器并不希望連接被關(guān)閉计呈,因此需要將 closeConnection 屬性設(shè)置為 false 來阻止默認(rèn)的關(guān)閉行為。例如:
<transactionManager type="MANAGED">
  <property name="closeConnection" value="false"/>
</transactionManager>

如果你正在使用 Spring + MyBatis征唬,則沒有必要配置事務(wù)管理器捌显,因?yàn)?Spring 模塊會(huì)使用自帶的管理器來覆蓋前面的配置。這兩種事務(wù)管理器類型都不需要設(shè)置任何屬性总寒。它們其實(shí)是類型別名扶歪,換句話說,你可以用 TransactionFactory 接口實(shí)現(xiàn)類的全限定名或類型別名代替它們摄闸。

public interface TransactionFactory {
  default void setProperties(Properties props) { // 從 3.5.2 開始击罪,該方法為默認(rèn)方法
    // 空實(shí)現(xiàn)
  }
  Transaction newTransaction(Connection conn);
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

在事務(wù)管理器實(shí)例化后,所有在 XML 中配置的屬性將會(huì)被傳遞給 setProperties() 方法贪薪。你的實(shí)現(xiàn)還需要?jiǎng)?chuàng)建一個(gè) Transaction 接口的實(shí)現(xiàn)類媳禁,這個(gè)接口也很簡(jiǎn)單:

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

使用這兩個(gè)接口,你可以完全自定義 MyBatis 對(duì)事務(wù)的處理画切。

數(shù)據(jù)源(dataSource)

dataSource 元素使用標(biāo)準(zhǔn)的 JDBC 數(shù)據(jù)源接口來配置 JDBC 連接對(duì)象的資源竣稽。

大多數(shù) MyBatis 應(yīng)用程序會(huì)按示例中的例子來配置數(shù)據(jù)源。雖然數(shù)據(jù)源配置是可選的霍弹,但如果要啟用延遲加載特性毫别,就必須配置數(shù)據(jù)源。

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

UNPOOLED– 這個(gè)數(shù)據(jù)源的實(shí)現(xiàn)會(huì)每次請(qǐng)求時(shí)打開和關(guān)閉連接典格。雖然有點(diǎn)慢岛宦,但對(duì)那些數(shù)據(jù)庫(kù)連接可用性要求不高的簡(jiǎn)單應(yīng)用程序來說,是一個(gè)很好的選擇耍缴。 性能表現(xiàn)則依賴于使用的數(shù)據(jù)庫(kù)砾肺,對(duì)某些數(shù)據(jù)庫(kù)來說,使用連接池并不重要防嗡,這個(gè)配置就很適合這種情形变汪。UNPOOLED 類型的數(shù)據(jù)源僅僅需要配置以下 5 種屬性:

  • driver – 這是 JDBC 驅(qū)動(dòng)的 Java 類全限定名(并不是 JDBC 驅(qū)動(dòng)中可能包含的數(shù)據(jù)源類)。
  • url – 這是數(shù)據(jù)庫(kù)的 JDBC URL 地址蚁趁。
  • username – 登錄數(shù)據(jù)庫(kù)的用戶名裙盾。
  • password – 登錄數(shù)據(jù)庫(kù)的密碼。
  • defaultTransactionIsolationLevel – 默認(rèn)的連接事務(wù)隔離級(jí)別。
  • defaultNetworkTimeout – 等待數(shù)據(jù)庫(kù)操作完成的默認(rèn)網(wǎng)絡(luò)超時(shí)時(shí)間(單位:毫秒)番官。查看 java.sql.Connection#setNetworkTimeout() 的 API 文檔以獲取更多信息庐完。

作為可選項(xiàng),你也可以傳遞屬性給數(shù)據(jù)庫(kù)驅(qū)動(dòng)徘熔。只需在屬性名加上“driver.”前綴即可门躯,例如:

  • driver.encoding=UTF8

這將通過 DriverManager.getConnection(url, driverProperties) 方法傳遞值為 UTF8encoding 屬性給數(shù)據(jù)庫(kù)驅(qū)動(dòng)。

POOLED– 這種數(shù)據(jù)源的實(shí)現(xiàn)利用“池”的概念將 JDBC 連接對(duì)象組織起來近顷,避免了創(chuàng)建新的連接實(shí)例時(shí)所必需的初始化和認(rèn)證時(shí)間生音。 這種處理方式很流行宁否,能使并發(fā) Web 應(yīng)用快速響應(yīng)請(qǐng)求窒升。

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

  • poolMaximumActiveConnections – 在任意時(shí)間可存在的活動(dòng)(正在使用)連接數(shù)量慕匠,默認(rèn)值:10
  • poolMaximumIdleConnections – 任意時(shí)間可能存在的空閑連接數(shù)饱须。
  • poolMaximumCheckoutTime – 在被強(qiáng)制返回之前,池中連接被檢出(checked out)時(shí)間台谊,默認(rèn)值:20000 毫秒(即 20 秒)
  • poolTimeToWait – 這是一個(gè)底層設(shè)置蓉媳,如果獲取連接花費(fèi)了相當(dāng)長(zhǎng)的時(shí)間,連接池會(huì)打印狀態(tài)日志并重新嘗試獲取一個(gè)連接(避免在誤配置的情況下一直失敗且不打印日志)锅铅,默認(rèn)值:20000 毫秒(即 20 秒)酪呻。
  • poolMaximumLocalBadConnectionTolerance – 這是一個(gè)關(guān)于壞連接容忍度的底層設(shè)置, 作用于每一個(gè)嘗試從緩存池獲取連接的線程盐须。 如果這個(gè)線程獲取到的是一個(gè)壞的連接玩荠,那么這個(gè)數(shù)據(jù)源允許這個(gè)線程嘗試重新獲取一個(gè)新的連接,但是這個(gè)重新嘗試的次數(shù)不應(yīng)該超過 poolMaximumIdleConnectionspoolMaximumLocalBadConnectionTolerance 之和贼邓。 默認(rèn)值:3(新增于 3.4.5)
  • poolPingQuery – 發(fā)送到數(shù)據(jù)庫(kù)的偵測(cè)查詢阶冈,用來檢驗(yàn)連接是否正常工作并準(zhǔn)備接受請(qǐng)求。默認(rèn)是“NO PING QUERY SET”塑径,這會(huì)導(dǎo)致多數(shù)數(shù)據(jù)庫(kù)驅(qū)動(dòng)出錯(cuò)時(shí)返回恰當(dāng)?shù)腻e(cuò)誤消息女坑。
  • poolPingEnabled – 是否啟用偵測(cè)查詢。若開啟统舀,需要設(shè)置 poolPingQuery 屬性為一個(gè)可執(zhí)行的 SQL 語(yǔ)句(最好是一個(gè)速度非炒移快的 SQL 語(yǔ)句),默認(rèn)值:false誉简。
  • poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的頻率绰筛。可以被設(shè)置為和數(shù)據(jù)庫(kù)連接超時(shí)時(shí)間一樣描融,來避免不必要的偵測(cè)铝噩,默認(rèn)值:0(即所有連接每一時(shí)刻都被偵測(cè) — 當(dāng)然僅當(dāng) poolPingEnabled 為 true 時(shí)適用)。

JNDI – 這個(gè)數(shù)據(jù)源實(shí)現(xiàn)是為了能在如 EJB 或應(yīng)用服務(wù)器這類容器中使用,容器可以集中或在外部配置數(shù)據(jù)源骏庸,然后放置一個(gè) JNDI 上下文的數(shù)據(jù)源引用毛甲。這種數(shù)據(jù)源配置只需要兩個(gè)屬性:

  • initial_context – 這個(gè)屬性用來在 InitialContext 中尋找上下文(即,initialContext.lookup(initial_context))具被。這是個(gè)可選屬性玻募,如果忽略,那么將會(huì)直接從 InitialContext 中尋找 data_source 屬性一姿。
  • data_source – 這是引用數(shù)據(jù)源實(shí)例位置的上下文路徑七咧。提供了 initial_context 配置時(shí)會(huì)在其返回的上下文中進(jìn)行查找,沒有提供時(shí)則直接在 InitialContext 中查找叮叹。

JNDI 可理解是一種仿 windows 注冊(cè)表形式的數(shù)據(jù)源艾栋。

和其他數(shù)據(jù)源配置類似,可以通過添加前綴“env.”直接把屬性傳遞給 InitialContext蛉顽。比如:

  • env.encoding=UTF8

這就會(huì)在 InitialContext 實(shí)例化時(shí)往它的構(gòu)造方法傳遞值為 UTF8encoding 屬性蝗砾。

你可以通過實(shí)現(xiàn)接口 org.apache.ibatis.datasource.DataSourceFactory 來使用第三方數(shù)據(jù)源實(shí)現(xiàn):

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

org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory 可被用作父類來構(gòu)建新的數(shù)據(jù)源適配器,比如下面這段插入 C3P0 數(shù)據(jù)源所必需的代碼:

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

public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {

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

為了令其工作携冤,記得在配置文件中為每個(gè)希望 MyBatis 調(diào)用的 setter 方法增加對(duì)應(yīng)的屬性悼粮。 下面是一個(gè)可以連接至 PostgreSQL 數(shù)據(jù)庫(kù)的例子:

<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>
8、databaseIdProvider(數(shù)據(jù)庫(kù)廠商標(biāo)識(shí))

MyBatis 可以根據(jù)不同的數(shù)據(jù)庫(kù)廠商執(zhí)行不同的語(yǔ)句曾棕,這種多廠商的支持是基于映射語(yǔ)句中的 databaseId 屬性扣猫。 MyBatis 會(huì)加載帶有匹配當(dāng)前數(shù)據(jù)庫(kù) databaseId 屬性和所有不帶 databaseId 屬性的語(yǔ)句。 如果同時(shí)找到帶有 databaseId 和不帶 databaseId 的相同語(yǔ)句翘地,則后者會(huì)被舍棄申尤。 為支持多廠商特性,只要像下面這樣在 mybatis-config.xml 文件中加入 databaseIdProvider 即可:

<databaseIdProvider type="DB_VENDOR" />

databaseIdProvider 對(duì)應(yīng)的 DB_VENDOR 實(shí)現(xiàn)會(huì)將 databaseId 設(shè)置為 DatabaseMetaData#getDatabaseProductName() 返回的字符串子眶。 由于通常情況下這些字符串都非常長(zhǎng)瀑凝,而且相同產(chǎn)品的不同版本會(huì)返回不同的值,你可能想通過設(shè)置屬性別名來使其變短:

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

在提供了屬性別名時(shí)臭杰,databaseIdProvider 的 DB_VENDOR 實(shí)現(xiàn)會(huì)將 databaseId 設(shè)置為數(shù)據(jù)庫(kù)產(chǎn)品名與屬性中的名稱第一個(gè)相匹配的值粤咪,如果沒有匹配的屬性槽卫,將會(huì)設(shè)置為 “null”被去。 在這個(gè)例子中申鱼,如果 getDatabaseProductName() 返回“Oracle (DataDirect)”黎比,databaseId 將被設(shè)置為“oracle”。

你可以通過實(shí)現(xiàn)接口 org.apache.ibatis.mapping.DatabaseIdProvider 并在 mybatis-config.xml 中注冊(cè)來構(gòu)建自己的 DatabaseIdProvider:

public interface DatabaseIdProvider {
  default void setProperties(Properties p) { // 從 3.5.2 開始温兼,該方法為默認(rèn)方法
    // 空實(shí)現(xiàn)
  }
  String getDatabaseId(DataSource dataSource) throws SQLException;
}
9俱恶、mappers(映射器)

既然 MyBatis 的行為已經(jīng)由上述元素配置完了究孕,我們現(xiàn)在就要來定義 SQL 映射語(yǔ)句了比搭。 但首先冠跷,我們需要告訴 MyBatis 到哪里去找到這些語(yǔ)句。 在自動(dòng)查找資源方面,Java 并沒有提供一個(gè)很好的解決方案蜜托,所以最好的辦法是直接告訴 MyBatis 到哪里去找映射文件抄囚。 你可以使用相對(duì)于類路徑的資源引用,或完全限定資源定位符(包括 file:/// 形式的 URL)橄务,或類名和包名等幔托。例如:

<!-- 使用相對(duì)于類路徑的資源引用 -->
<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>
<!-- 使用映射器接口實(shí)現(xiàn)類的完全限定類名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 將包內(nèi)的映射器接口實(shí)現(xiàn)全部注冊(cè)為映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

XML 映射文件

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

對(duì)于 Mapper 具體的映射配置文件重挑,是 Mybatis 最復(fù)雜、最核心的組件棠涮,其中的標(biāo)簽內(nèi)容體系也是特別詳實(shí)谬哀,包括它的參數(shù)類型、動(dòng)態(tài)SQL故爵、定義SQL玻粪、緩存信息等等隅津,我們?cè)谙乱黄恼轮性龠M(jìn)行梳理討論诬垂,這里我們簡(jiǎn)單引出。

總結(jié)

原本我計(jì)劃把核心配置文件和映射器 mapper 文件放一塊講伦仍,但是發(fā)現(xiàn)內(nèi)容太多太多了结窘,基本核心配置文件就已經(jīng)講得有點(diǎn)拖堂了,雖然這幾大頂級(jí)標(biāo)簽使用起來已經(jīng)毫不費(fèi)力充蓝。SQL 映射器配置文件,我們后續(xù)更新谓苟,這塊基本是和我們?nèi)粘4蚪坏雷罡哳l的操作官脓。

本篇完,本系列下一篇我們講《 Mybatis系列全解(五):全網(wǎng)最全涝焙!詳解Mybatis的Mapper映射文件 》卑笨。

BIU ~ 文章持續(xù)更新,微信搜索「潘潘和他的朋友們」第一時(shí)間閱讀仑撞,隨時(shí)有驚喜赤兴。本文會(huì)在 GitHub https://github.com/JavaWorld 收錄,熱騰騰的技術(shù)隧哮、框架桶良、面經(jīng)、解決方案沮翔,我們都會(huì)以最美的姿勢(shì)第一時(shí)間送達(dá)陨帆,歡迎 Star。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市疲牵,隨后出現(xiàn)的幾起案子岸浑,更是在濱河造成了極大的恐慌,老刑警劉巖瑰步,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矢洲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡缩焦,警方通過查閱死者的電腦和手機(jī)读虏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來袁滥,“玉大人盖桥,你說我怎么就攤上這事√夥” “怎么了揩徊?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)嵌赠。 經(jīng)常有香客問我塑荒,道長(zhǎng),這世上最難降的妖魔是什么姜挺? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任齿税,我火速辦了婚禮,結(jié)果婚禮上炊豪,老公的妹妹穿的比我還像新娘凌箕。我一直安慰自己,他們只是感情好词渤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布牵舱。 她就那樣靜靜地躺著,像睡著了一般缺虐。 火紅的嫁衣襯著肌膚如雪芜壁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天志笼,我揣著相機(jī)與錄音沿盅,去河邊找鬼。 笑死纫溃,一個(gè)胖子當(dāng)著我的面吹牛腰涧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播紊浩,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼窖铡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼疗锐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起费彼,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤滑臊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后箍铲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雇卷,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年颠猴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了关划。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翘瓮,死狀恐怖贮折,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情资盅,我是刑警寧澤调榄,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站呵扛,受9級(jí)特大地震影響每庆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜择份,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一扣孟、第九天 我趴在偏房一處隱蔽的房頂上張望烫堤。 院中可真熱鬧荣赶,春花似錦、人聲如沸鸽斟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)富蓄。三九已至剩燥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間立倍,已是汗流浹背灭红。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留口注,地道東北人变擒。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像寝志,于是被迫代替她去往敵國(guó)和親娇斑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子策添,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容