在第一章中,我們簡單的搭建了一套可以運行的mybatis應用,當時在mybatis-config.xml中的配置一帶而過,今天在本章,詳細的解讀一下mybatis的核心配置文件中的內容,首先,附上一張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(配置)
- properties(屬性)
- settings(設置)
- typeAliases(類型別名)
- typeHandlers(類型處理器)
- objectFactory(對象工廠)
- plugins(插件)
- environments(環境配置)
- environment(環境變量)
- transactionManager(事務管理器)
- dataSource(數據源)
- environment(環境變量)
- databaseIdProvider(數據庫廠商標識)
- mappers(映射器)
首先是配置文件的頭文件,它主要導入了mybatis的命名空間,表示該配置文件是屬於mybatis的配置文件。
configuration標籤是所有標籤的祖標籤,接下來學習的所有標籤都需要在它的內部完成,類比與前端的html標籤。
properties標籤標識屬性,我們可以通過該標籤引入一些外部的屬性,或者在該標籤內定義一些屬性值,在mybatis的配置文件的其他標籤中都可以使用,我們拿配置數據源的案例來深入的理解一下這個標籤。
配置目錄:
config.properties:
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://188.131.xxx.xxx:3306/dbName?useUnicode=true&characterEncoding=utf8&useSSL=true
mybatis-config.xml:
<properties resource="config.properties"> <property name="username" value="root"/> <property name="password" value="root"/> </properties>
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <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>
其中mybatis-config.xml是我們mybatis的核心配置文件,config.properties是我們的外部配置文件,我們都知道,如果想獲取數據庫實例的數據源,需要4個必要參數:數據庫啓動,數據庫地址,數據庫用戶名和數據庫密碼,我們在外部配置文件中定義了數據的驅動和地址,可以通過properties標籤中的resource屬性來引入這個外部配置文件,我們又在properties標籤中的子標籤property中定義了數據庫的用戶名和數據庫密碼,他們是以鍵值對的方式來定義的,定義好我們的屬性後,就可以在mybatis-config.xml中的任何位置時候這些變量了,可以通過${key}的方式來獲取到定義的變量值。那麼如果我引入的外部配置文件中的屬性和properties內部定義的屬性重名會怎麼樣?mybatis加載配置時是有優先級的,外部配置文件的優先級大於內部標籤的優先級。
settings標籤是 MyBatis 中極爲重要的調整設置,它們會改變 MyBatis 的運行時行爲。 下表描述了設置中各項設置的含義、默認值等。
設置名 | 描述 | 有效值 | 默認值 |
---|---|---|---|
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, 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(deprecated since 3.5.9) | 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 | Specifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type (or value ) attribute on sql provider annotation(e.g. @SelectProvider ), when these attribute was omitted. |
A type alias or fully qualified class name | Not set |
nullableOnForEach | Specifies the default value of 'nullable' attribute on 'foreach' tag. (Since 3.5.9) | true | false | false |
<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"/> </settings>
其中有些比較實用常用的配置,我們會單獨拉出來篇幅講解,這裏不做過多介紹。
typeAliases標籤可爲 Java 類型設置一個縮寫名字。 它僅用於 XML 配置,意在降低冗餘的全限定類名書寫。例如:
<typeAliases> <typeAlias alias="Student" type="com.pojo.Student"/> <typeAlias alias="Teacher" type="com.pojo.Teacher"/> </typeAliases>
這樣配置後,我們可以在xml中任何使用domain.Student的地方,都可以替換成Student。當然,實際開發中會涉及到很多很多的實體類,如果這樣配置,我們要給每一個java實體類都取一個別名,會很麻煩,爲了解決這種場景,我們也可以指定一個包名,MyBatis 會在包名下面搜索需要的 Java Bean,比如:
<typeAliases> <package name="com.pojo"/> </typeAliases>
每一個在包 com.pojo
中的 Java Bean,在沒有註解的情況下,會使用 Bean 的首字母小寫的非限定類名來作爲它的別名。 比如 .com.pojo.Student
的別名爲 student;若有註解,則別名爲其註解值。見下面的例子:
@Alias("student") public class Student { ... }
定義好別名之後,最頻繁使用的地方就是mapper.xml中的返回集或者請求參數集的映射了,例如:
<select id="select" resultType="Student"> select * from student </select>
resultType中就是使用的別名來找到相應的java類。
mybatis也爲我們內置了很多java中常用的類的一些別名,如map,list等,具體可以參考其官方文檔進行查閱。
typeHandlers標籤爲類型處理器,MyBatis 在設置預處理語句(PreparedStatement)中的參數或從結果集中取出一個值時, 都會用類型處理器將獲取到的值以合適的方式轉換成 Java 類型,達到由數據庫字段映射到相應的java baen屬性的目的。mybatis也內置了很多類型轉換器,如下:
類型處理器 | Java 類型 | JDBC 類型 |
---|---|---|
BooleanTypeHandler |
java.lang.Boolean , boolean |
數據庫兼容的 BOOLEAN |
ByteTypeHandler |
java.lang.Byte , byte |
數據庫兼容的 NUMERIC 或 BYTE |
ShortTypeHandler |
java.lang.Short , short |
數據庫兼容的 NUMERIC 或 SMALLINT |
IntegerTypeHandler |
java.lang.Integer , int |
數據庫兼容的 NUMERIC 或 INTEGER |
LongTypeHandler |
java.lang.Long , long |
數據庫兼容的 NUMERIC 或 BIGINT |
FloatTypeHandler |
java.lang.Float , float |
數據庫兼容的 NUMERIC 或 FLOAT |
DoubleTypeHandler |
java.lang.Double , double |
數據庫兼容的 NUMERIC 或 DOUBLE |
BigDecimalTypeHandler |
java.math.BigDecimal |
數據庫兼容的 NUMERIC 或 DECIMAL |
StringTypeHandler |
java.lang.String |
CHAR , VARCHAR |
ClobReaderTypeHandler |
java.io.Reader |
- |
ClobTypeHandler |
java.lang.String |
CLOB , LONGVARCHAR |
NStringTypeHandler |
java.lang.String |
NVARCHAR , NCHAR |
NClobTypeHandler |
java.lang.String |
NCLOB |
BlobInputStreamTypeHandler |
java.io.InputStream |
- |
ByteArrayTypeHandler |
byte[] |
數據庫兼容的字節流類型 |
BlobTypeHandler |
byte[] |
BLOB , LONGVARBINARY |
DateTypeHandler |
java.util.Date |
TIMESTAMP |
DateOnlyTypeHandler |
java.util.Date |
DATE |
TimeOnlyTypeHandler |
java.util.Date |
TIME |
SqlTimestampTypeHandler |
java.sql.Timestamp |
TIMESTAMP |
SqlDateTypeHandler |
java.sql.Date |
DATE |
SqlTimeTypeHandler |
java.sql.Time |
TIME |
ObjectTypeHandler |
Any | OTHER 或未指定類型 |
EnumTypeHandler |
Enumeration Type | VARCHAR 或任何兼容的字符串類型,用來存儲枚舉的名稱(而不是索引序數值) |
EnumOrdinalTypeHandler |
Enumeration Type | 任何兼容的 NUMERIC 或 DOUBLE 類型,用來存儲枚舉的序數值(而不是名稱)。 |
SqlxmlTypeHandler |
java.lang.String |
SQLXML |
InstantTypeHandler |
java.time.Instant |
TIMESTAMP |
LocalDateTimeTypeHandler |
java.time.LocalDateTime |
TIMESTAMP |
LocalDateTypeHandler |
java.time.LocalDate |
DATE |
LocalTimeTypeHandler |
java.time.LocalTime |
TIME |
OffsetDateTimeTypeHandler |
java.time.OffsetDateTime |
TIMESTAMP |
OffsetTimeTypeHandler |
java.time.OffsetTime |
TIME |
ZonedDateTimeTypeHandler |
java.time.ZonedDateTime |
TIMESTAMP |
YearTypeHandler |
java.time.Year |
INTEGER |
MonthTypeHandler |
java.time.Month |
INTEGER |
YearMonthTypeHandler |
java.time.YearMonth |
VARCHAR 或 LONGVARCHAR |
JapaneseDateTypeHandler |
java.time.chrono.JapaneseDate |
DATE |
其實mybatis內置的類型處理器基本上能滿足我們大部分的需求,其中枚舉類型處理器值得一提,因爲枚舉類型mybatis爲我們提供了兩種類型處理器:EnumTypeHandler和EnumOrdinalTypeHandler
默認使用EnumTypeHandler。
接下來,我們通過一個小例子來重點理解一下這兩個枚舉類型的處理器。在我們實際開發中,數據庫的一些字段我們不會存儲真實的含義,而是存儲數字來代表其含義,如:student表中的sex屬性表示性別,但實際存儲記錄的時候該屬性我們很少直接存“男”或者“女”,而是通過數字0表示男,1表示女。在java代碼中使用這些數字的時候,很多同學都會直接拿到該字段的值,進行硬編碼,這樣寫當然沒有問題,但是大大降低了我們代碼的可讀性,以及增加了後續的代碼維護工作,其實最優的方案是,只要數據庫中存在使用多個數字代表多個含義的時候,我們都應該將這些含義在java代碼中定義一個相應的枚舉類型,用該枚舉類型與數據庫中的該字段進行映射對應。默認情況下,MyBatis 會利用 EnumTypeHandler
來把 Enum
值轉換成對應的名字。注意 EnumTypeHandler
在某種意義上來說是比較特別的,其它的處理器只針對某個特定的類,而它不同,它會處理任意繼承了 Enum
的類。不過,我們可能不想存儲名字,相反我們的 DBA 會堅持使用整形值代碼。那也一樣簡單:在配置文件中把 EnumOrdinalTypeHandler
加到 typeHandlers
中即可, 這樣每個 RoundingMode
將通過他們的序數值來映射成對應的整形數值。配置方式如下:
<typeHandlers> <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.pojo.EnumDemo"/> </typeHandlers>
這樣可以優雅是實現我們的java代碼,而且數據庫也可以保證使用整型值代碼(雖然比硬編碼的方式麻煩,但很優雅,便於維護,可讀性大大增強)。
objectFactory標籤表示對象工廠,每次 MyBatis 創建結果對象的新實例時,它都會使用一個對象工廠(ObjectFactory)實例來完成實例化工作。 默認的對象工廠需要做的僅僅是實例化目標類,要麼通過默認無參構造方法,要麼通過存在的參數映射來調用帶有參數的構造方法。 那麼如果在實際開發場景中,假如我們的java bean中某些屬性在數據表中沒有字段或需要特殊處理,這時候該怎麼處理呢?答案是用自定義對象工廠。
首先我們需要實現一下自定義的對象工廠,一般來說,我們可以直接繼承DefaultObjectFactory類,實現其中的某些方法。也可以直接實現ObjectFactory接口。
我們這裏選擇直接繼承DefaultObjectFactory類即可。這裏重寫其中的create方法。這裏的邏輯就是當MyBatis查詢到結果(存放在ResultSet中),然後每次取一行數據封裝成POJO對象就會使用對象工廠。這裏create方法進行攔截,判斷是我們java baen類型,則設置某些屬性值爲xxx。其他對象則直接調用默認的create方法(DefaultObjectFactory中方法)。
public class DivObjectFactory extends DefaultObjectFactory { @Override public <T> T create(Class<T> type) { if(type== DivLayoutInfo.class){ DivLayoutInfo divLayoutInfo = super.create(DivLayoutInfo.class); divLayoutInfo.setModeName("哈哈哈"); } return super.create(type); }
接下來配置對象工廠:
<objectFactory type="mybatis.config.DivObjectFactory" />
這裏需要注意的是,在覈心配置文件中的標籤是有順序要求的,這裏的ObjectFactory標籤是放在typeHandler標籤之後的。
這樣配置了自定義對象工廠後,我麼查詢出來的divLayoutInfo對象的modeName值不管從數據庫裏查出來的是什麼都會被賦值成“哈哈哈”,總的來說,當我們創建的對象和數據庫表字段一致時,由默認的對象工廠幫我們創建。當我們配置了相關的對象工程(objectFactory標籤),則會調用我們自定義的對象工廠進行處理,相當於做了攔截。
- environments(環境配置)
- environment(環境變量)
- transactionManager(事務管理器)
- dataSource(數據源)
- environment(環境變量)
我們在介紹peoperties標籤的時候就已經見過這幾個標籤了,他們是定義數據庫的是具有,事務等信息的,主要通過這些標籤來定義數據庫的連接信息,數據庫連接池,事務等。
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <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>
由於該配置在mybatis的配置中及其重要,我們會單獨拿一個章節來具體分析一下這些配置項的各種參數的含義。
databaseIdProvider標籤,在實際開發中,我們的項目想要做的兼容性更強,就需要支持多數據源,多數據庫,如果想同時支持mysql和oracle的話,就需要使用該標籤,它可以根據不同的數據庫廠商執行不同的語句,這種多廠商的支持是基於映射語句中的 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”。
設置好這些之後,我們就可以在mapper.xml中對於同一個mapper接口同時寫兩個sql語句(mysql的和oracle的),通過databaseId 屬性來定義該sql屬於哪種數據庫。
<select id="getInfo" resultType="string" databaseId="mysql"> select * from div_layout_info </select> <select id="getInfo" resultType="string" databaseId="oracle"> select * from div_layout_info </select>
mappers標籤(映射器),既然 MyBatis 的行爲已經由上述元素配置完了,我們現在就要來定義 SQL 映射語句了。 但首先,我們需要告訴 MyBatis 到哪裏去找到這些語句。 在自動查找資源方面,Java 並沒有提供一個很好的解決方案,所以最好的辦法是直接告訴 MyBatis 到哪裏去找映射文件。 我們可以使用相對於類路徑的資源引用,或完全限定資源定位符(包括 file:///
形式的 URL),或類名和包名等。
<!-- 使用相對於類路徑的資源引用 --> <mappers> <mapper resource="/mapper/StudentMapper.xml"/> <mapper resource="/mapper/Teacher.xml"/> </mappers>
<!-- 使用映射器接口實現類的完全限定類名 --> <mappers> <mapper class="com.dao.StudentMapper"/> <mapper class="com.dao.TeacherMapper"/> </mappers>
<!-- 將包內的映射器接口實現全部註冊爲映射器 --> <mappers> <package name="com.dao"/> </mappers>
這些配置會告訴 MyBatis 去哪裏找映射文件,剩下的細節就應該是每個 SQL 映射文件了,也就是接下來我們要討論的。
很顯然,第三種會更加的簡潔,我們把所有dao層定義的接口統一放到某個包下,註冊mapper的時候只需要掃描該包下的所有類即可,這也是約定大於配置思想的一個實現。
最後說一句,mybatis的所有配置要嚴格地按照上面配置目錄的順序來配置,否則可能會出現配置不生效設置啓動報錯的各種問題。