一、為什麼要使用配置檔案
試想,如果沒有配置檔案,我們的應用程式將只能沿著固定的姿態執行,幾乎不能做任何動態的調整,那麼這不是一套完美的設計,因為我們希望擁有更寬更靈活的操作空間和更多的相容度,同時也能解決硬編碼等問題,所以我們需要有配置檔案,對應用程式進行引數預設和設定初始化工作。
那我們為何鍾情XML?
首先,當然是 XML 配置檔案本身就足夠優秀,格式規範,儲存小,跨平臺,讀取快...等等,所謂窈窕淑女,誰人不愛。
其次,也是一個重要影響因素,就是各大領域大佬的支援,像微軟、像Java系...等等,世上本無路,只是走的人多了,也就成了路 ( 這句話是魯迅老先生說的)。
所以,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> 與 ,
其餘標籤元素都是 Mybatis 的核心配置屬性 :
以上基本能夠清晰看明白 Mybatis 配置檔案的層次結構關係,我們簡單畫一張腦圖:
基本是需要我們掌握 9 大頂級元素配置,其中標記 橘紅色 的屬性配置,由於涉及 外掛 和 動態SQL ,外掛配置可以應用於分頁與功能增強等,動態SQL例如 if 標籤、where 標籤、foreach標籤等,初步理解為應用於SQL語句拼接。這兩塊屬於 Mybatis 的兩個特性,我們後續單獨詳細進行梳理討論。
三、XML 核心配置我們的核心配置檔案 configuration(配置)作為最頂級節點,其餘 9 大屬性都必須巢狀在其內,對於內部 9 大節點,我們逐一講解:
1、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.Driverurl=jdbc\:mysql\://127.0.0.1\:3306/myDBusername=rootpassword=123456
使用時我們把屬性檔案引入,並使用檔案中定義的佔位符,例如 db.driver :
但是問題來了,當我們既使用 *.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 同時存在,會丟擲異常禁止。
// 構建屬性物件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"); // 傳遞屬性構建 SqlSessionFactorySqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);複製程式碼
那麼這三種方式都允許配置,那在屬性配置重複的情況下,優先級別是怎樣呢?
properties 優先順序
1、第一優先順序:在 Java 程式碼中構建的 properties 屬性物件;
2、第二優先順序:透過屬性 resource 或 url 讀取到的本地檔案或遠端檔案;
3、第三優先順序:直接在 properties 內部子標籤元素 property 中設定的屬性。
注意,在實際開發中,為了避免給後期維護造成困擾,建議使用單一種配置方式。
2、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 支援了特別多功能支援,其實常規開發中使用到的屬性項不會特別多,除非專案有特殊要求,所以建議大家把這些設定當做字典即可,不必詳記 每一個屬性使用,需要時翻閱研讀。
3、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 在註冊別名的時候會全部轉為小寫字母進行儲存,另外以上列表 無需牢記,僅僅在需要使用的時候查閱即可,基本也都可以看得明白。
4、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) ,可以在以上原始碼上看到新增支援。
一般,你可以重寫已有的型別處理器,
或根據業務需要建立你自己的型別處理器,
以處理不支援的型別或非標準的型別。
具體做法為:
1、實現 org.apache.ibatis.type.TypeHandler 介面;
2、繼承 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;}
從方法名 setParameter 和 getResult 我們就可以知道,是發生在預編譯時設定引數(增刪改查傳入引數)與查詢結果集後轉換為 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 類做對映轉換(註解和泛型已體現)。
其次,在核心配置檔案中設定型別處理器:
這裡,自定義型別處理器將會覆蓋已有的處理 Java String 型別的屬性以及 VARCHAR 型別的引數和結果的型別處理器,基本以上步驟就已經自定了 JdbcType.VARCHAR 與 String類做對映轉換。
其實到這裡,我們基本也就完成了型別處理器的自定義轉換,但是有一種情況,就是我們希望我們自定義的型別處理器只處理某一個 Java 實體中的 JdbcType.VARCHAR 與 String 類對映轉換,其它實體的處理還是使用系統內建的轉換,很簡單,我們只需要把以上兩步都去掉,在自定義型別處理類的註解@javaType和@MappedJdbcTypes都移除,配置檔案中把 typehandler 屬性配置移除,直接在對映檔案中編寫:
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 會預設匹配系統內建的型別處理器。
另外,當我們自定義很多型別處理器時,系統支援配置包掃描的方式查詢型別處理器:
你可以建立能夠處理多個類的泛型型別處理器。為了使用泛型型別處理器, 需要增加一個接受該類的 class 作為引數的構造器,這樣 MyBatis 會在構造一個型別處理器例項的時候傳入一個具體的類。
//GenericTypeHandler.javapublic 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 將透過他們的序數值來對映成對應的整形數值。
自動對映器(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。
5、objectFactory(物件工廠)
每次 MyBatis 建立結果物件的新例項時,它都會使用一個物件工廠(ObjectFactory)例項來完成例項化工作。 預設的物件工廠需要做的僅僅是例項化目標類,要麼透過預設無參構造方法,要麼透過存在的引數對映來呼叫帶有引數的構造方法。 如果想覆蓋物件工廠的預設行為,可以透過建立自己的物件工廠來實現。比如:
正常情況下我們不需要使用到,或者說不建議使用,除非業務上確實需要對一個特殊實體初始構造做一個預設屬性值配置等處理,其餘情況不推薦使用,避免產生不可控風險。
6、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 介面,並指定想要攔截的類,方法,引數(由於有多型的情況)即可。
覆蓋配置類 「 謹慎使用,存在風險 」
除了用外掛來修改 MyBatis 核心行為以外,還可以透過完全覆蓋配置類來達到目的。只需繼承配置類後覆蓋其中的某個方法,再把它傳遞到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,這可能會極大影響 MyBatis 的行為,務請慎之又慎。
7、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) 方法傳遞值為 UTF8 的 encoding 屬性給資料庫驅動。
POOLED– 這種資料來源的實現利用“池”的概念將 JDBC 連線物件組織起來,避免了建立新的連線例項時所必需的初始化和認證時間。 這種處理方式很流行,能使併發 Web 應用快速響應請求。
除了上述提到 UNPOOLED 下的屬性外,還有更多屬性用來配置 POOLED 的資料來源:
poolMaximumActiveConnections – 在任意時間可存在的活動(正在使用)連線數量,預設值:10poolMaximumIdleConnections – 任意時間可能存在的空閒連線數。poolMaximumCheckoutTime – 在被強制返回之前,池中連線被檢出(checked out)時間,預設值:20000 毫秒(即 20 秒)poolTimeToWait – 這是一個底層設定,如果獲取連線花費了相當長的時間,連線池會列印狀態日誌並重新嘗試獲取一個連線(避免在誤配置的情況下一直失敗且不列印日誌),預設值:20000 毫秒(即 20 秒)。poolMaximumLocalBadConnectionTolerance – 這是一個關於壞連線容忍度的底層設定, 作用於每一個嘗試從快取池獲取連線的執行緒。 如果這個執行緒獲取到的是一個壞的連線,那麼這個資料來源允許這個執行緒嘗試重新獲取一個新的連線,但是這個重新嘗試的次數不應該超過 poolMaximumIdleConnections 與 poolMaximumLocalBadConnectionTolerance 之和。 預設值: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 例項化時往它的構造方法傳遞值為 UTF8 的 encoding 屬性。
你可以透過實現介面 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>
8、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;}
9、mappers(對映器)
既然 MyBatis 的行為已經由上述元素配置完了,我們現在就要來定義 SQL 對映語句了。 但首先,我們需要告訴 MyBatis 到哪裡去找到這些語句。 在自動查詢資源方面,Java 並沒有提供一個很好的解決方案,所以最好的辦法是直接告訴 MyBatis 到哪裡去找對映檔案。 你可以使用相對於類路徑的資源引用,或完全限定資源定位符(包括 file:/// 形式的 URL),或類名和包名等。例如:
五、總結原本我計劃把核心配置檔案和對映器 mapper 檔案放一塊講,但是發現內容太多太多了,基本核心配置檔案就已經講得有點拖堂了,雖然這幾大頂級標籤使用起來已經毫不費力。SQL 對映器配置檔案,我們後續更新,這塊基本是和我們日常打交道最高頻的操作。
連結:https://juejin.cn/post/6903905087333400584