在使用mybatis框架時(shí),首先導(dǎo)入其對(duì)應(yīng)的jar包,并進(jìn)行相應(yīng)的配置,所以得對(duì)配置文件的每個(gè)參數(shù)都得了解。一個(gè)完全的mybatis配置文件結(jié)構(gòu)如下:
<?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></properties>
<!-- 設(shè)置:定義mybatis的一些全局性設(shè)置 -->
<settings>
<!-- 具體的參數(shù)名和參數(shù)值 -->
<setting name="" value=""/>
</settings>
<!-- 類型名稱:為一些類定義別名 -->
<typeAliases></typeAliases>
<!-- 類型處理器:定義Java類型與數(shù)據(jù)庫中的數(shù)據(jù)類型之間的轉(zhuǎn)換關(guān)系 -->
<typeHandlers></typeHandlers>
<!-- 對(duì)象工廠 -->
<objectFactory type=""></objectFactory>
<!-- 插件:mybatis的插件,插件可以修改mybatis的內(nèi)部運(yùn)行規(guī)則 -->
<plugins>
<plugin interceptor=""></plugin>
</plugins>
<!-- 環(huán)境:配置mybatis的環(huán)境 -->
<environments default="">
<!-- 環(huán)境變量:可以配置多個(gè)環(huán)境變量,比如使用多數(shù)據(jù)源時(shí),就需要配置多個(gè)環(huán)境變量 -->
<environment id="">
<!-- 事務(wù)管理器 -->
<transactionManager type=""></transactionManager>
<!-- 數(shù)據(jù)源 -->
<dataSource type=""></dataSource>
</environment>
</environments>
<!-- 數(shù)據(jù)庫廠商標(biāo)識(shí) -->
<databaseIdProvider type=""></databaseIdProvider>
<!-- 映射器:指定映射文件或者映射類 -->
<mappers></mappers>
</configuration>
properties
properties元素主要是用來定義配置外在化,比如數(shù)據(jù)庫的連接屬性等。這些屬性都是可外部配置且可動(dòng)態(tài)替換的,既可以在典型的Java屬性文件中配置,亦可以通過properties元素的子元素來傳遞。例如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
其中的屬性就可以在整個(gè)配置文件中使用來替換需要?jiǎng)討B(tài)配置的屬性值。比如在數(shù)據(jù)源中使用的例子:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
這個(gè)例子中的username和password將會(huì)由properties元素中設(shè)置的相應(yīng)值來替換。driver和url屬性將會(huì)由config.properties文件中對(duì)應(yīng)的值來替換。這樣就為配置提供了諸多靈活選擇。屬性也可以被傳遞到SqlSessionBuilder.build()方法中。例如:
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, props);
// ... or ...
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, props);
但是,這也就涉及到了優(yōu)先級(jí)的問題,如果屬性不只在一個(gè)地方配置,那么mybatis將會(huì)按照下面的順序來加載:
在properties元素體內(nèi)指定的屬性首先被讀取。
然后根據(jù)properties元素中的resource屬性讀取類路徑下屬性文件或根據(jù)url屬性指定的路徑讀取屬性文件,并覆蓋已讀取的同名屬性。
最后讀取作為方法參數(shù)傳遞的屬性,并覆蓋已讀取的同名屬性。
因此,通過方法參數(shù)傳遞的屬性具有最高優(yōu)先級(jí),resource/url屬性中指定的配置文件次之,最低優(yōu)先級(jí)的是properties屬性中指定的屬性。
settings
setting是指定MyBatis的一些全局配置屬性,這是MyBatis中極為重要的調(diào)整設(shè)置,它們會(huì)改變MyBatis的運(yùn)行時(shí)行為,所以我們需要清楚的知道這些屬性的作用及默認(rèn)值。
設(shè)置參數(shù) 描述 有效值 默認(rèn)值
cacheEnabled 該配置影響的所有映射器中配置的緩存的全局開關(guān) true | false 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 false
aggressiveLazyLoading 當(dāng)啟用時(shí),對(duì)任意延遲屬性的調(diào)用會(huì)使帶有延遲加載屬性的對(duì)象完整加載;反之,每種屬性將會(huì)按需加載。 true | false true
multipleResultSetsEnabled 是否允許單一語句返回多結(jié)果集(需要兼容驅(qū)動(dòng))。 true | false true
useColumnLabel 使用列標(biāo)簽代替列名。不同的驅(qū)動(dòng)在這方面會(huì)有不同的表現(xiàn), 具體可參考相關(guān)驅(qū)動(dòng)文檔或通過測(cè)試這兩種不同的模式來觀察所用驅(qū)動(dòng)的結(jié)果。 true | false true
useGeneratedKeys 允許 JDBC 支持自動(dòng)生成主鍵,需要驅(qū)動(dòng)兼容。 如果設(shè)置為 true 則這個(gè)設(shè)置強(qiáng)制使用自動(dòng)生成主鍵,盡管一些驅(qū)動(dòng)不能兼容但仍可正常工作(比如 Derby)。 true | false False
autoMappingBehavior 指定 MyBatis 應(yīng)如何自動(dòng)映射列到字段或?qū)傩浴?NONE 表示取消自動(dòng)映射;PARTIAL 只會(huì)自動(dòng)映射沒有定義嵌套結(jié)果集映射的結(jié)果集。 FULL 會(huì)自動(dòng)映射任意復(fù)雜的結(jié)果集(無論是否嵌套)。 NONE, PARTIAL, FULL PARTIAL
defaultExecutorType 配置默認(rèn)的執(zhí)行器。SIMPLE 就是普通的執(zhí)行器;REUSE 執(zhí)行器會(huì)重用預(yù)處理語句(prepared statements); BATCH 執(zhí)行器將重用語句并執(zhí)行批量更新。 SIMPLE REUSE BATCH SIMPLE
defaultStatementTimeout 設(shè)置超時(shí)時(shí)間,它決定驅(qū)動(dòng)等待數(shù)據(jù)庫響應(yīng)的秒數(shù)。 Any positive integer Not Set (null)
defaultFetchSize Sets the driver a hint as to control fetching size for return results. This parameter value can be override by a query setting. Any positive integer Not Set (null)
safeRowBoundsEnabled 允許在嵌套語句中使用分頁(RowBounds)。 true | false False
mapUnderscoreToCamelCase 是否開啟自動(dòng)駝峰命名規(guī)則(camel case)映射,即從經(jīng)典數(shù)據(jù)庫列名 A_COLUMN 到經(jīng)典 Java 屬性名 aColumn 的類似映射。 true | false False
localCacheScope MyBatis 利用本地緩存機(jī)制(Local Cache)防止循環(huán)引用(circular references)和加速重復(fù)嵌套查詢。 默認(rèn)值為 SESSION,這種情況下會(huì)緩存一個(gè)會(huì)話中執(zhí)行的所有查詢。 若設(shè)置值為 STATEMENT,本地會(huì)話僅用在語句執(zhí)行上,對(duì)相同 SqlSession 的不同調(diào)用將不會(huì)共享數(shù)據(jù)。 SESSION | STATEMENT SESSION
jdbcTypeForNull 當(dāng)沒有為參數(shù)提供特定的 JDBC 類型時(shí),為空值指定 JDBC 類型。 某些驅(qū)動(dòng)需要指定列的 JDBC 類型,多數(shù)情況直接用一般類型即可,比如 NULL、VARCHAR 或 OTHER。 JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER OTHER
lazyLoadTriggerMethods 指定哪個(gè)對(duì)象的方法觸發(fā)一次延遲加載。 A method name list separated by commas equals,clone,hashCode,toString
defaultScriptingLanguage 指定動(dòng)態(tài) SQL 生成的默認(rèn)語言。 A type alias or fully qualified class name.
org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver
callSettersOnNulls 指定當(dāng)結(jié)果集中值為 null 的時(shí)候是否調(diào)用映射對(duì)象的 setter(map 對(duì)象時(shí)為 put)方法,這對(duì)于有 Map.keySet() 依賴或 null 值初始化的時(shí)候是有用的。注意基本類型(int、boolean等)是不能設(shè)置成 null 的。 true | false false
logPrefix 指定 MyBatis 增加到日志名稱的前綴。 Any String Not set
logImpl 指定 MyBatis 所用日志的具體實(shí)現(xiàn),未指定時(shí)將自動(dòng)查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING Not set
proxyFactory 指定 Mybatis 創(chuàng)建具有延遲加載能力的對(duì)象所用到的代理工具。 CGLIB | JAVASSIST JAVASSIST (MyBatis 3.3 or above)
一個(gè)完整的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="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類型設(shè)置一個(gè)短的名字。它只和xml配置有關(guān),存在的意義僅在于用來減少類完全限定名的冗余,例如:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
當(dāng)這樣配置時(shí),Blog可以用在任何使用domain.blog.Blog的地方。
也可以指定一個(gè)包名,MyBatis會(huì)在包名下搜索需要的JavaBean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
每一個(gè)在包domain.blog中的JavaBean,在沒有注解的情況下,會(huì)使用Bean的首字母小寫的非限類名來作為它的別名。比如domain.blog.Author的別名為author;若有注解,則別名為注解值??聪旅娴睦樱?/p>
@Alias("author") public class Author
{
...}
已經(jīng)為許多常見的Java類型內(nèi)建了相應(yīng)的類型別名。它們都是大小寫不敏感的,需要注意的是有基本類型名稱重復(fù)導(dǎo)致的特殊處理。
別名 映射的類型
_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
typeHandlers
無論是 MyBatis 在預(yù)處理語句(PreparedStatement)中設(shè)置一個(gè)參數(shù)時(shí),還是從結(jié)果集中取出一個(gè)值時(shí), 都會(huì)用類型處理器將獲取的值以合適的方式轉(zhuǎn)換成 Java 類型。下表描述了一些默認(rèn)的類型處理器。
類型處理器 Java 類型 JDBC 類型
BooleanTypeHandler java.lang.Boolean, boolean 數(shù)據(jù)庫兼容的 BOOLEAN
ByteTypeHandler java.lang.Byte, byte 數(shù)據(jù)庫兼容的 NUMERIC 或 BYTE
ShortTypeHandler java.lang.Short, short 數(shù)據(jù)庫兼容的 NUMERIC 或 SHORT INTEGER
IntegerTypeHandler java.lang.Integer, int 數(shù)據(jù)庫兼容的 NUMERIC 或 INTEGER
LongTypeHandler java.lang.Long, long 數(shù)據(jù)庫兼容的 NUMERIC 或 LONG INTEGER
FloatTypeHandler java.lang.Float, float 數(shù)據(jù)庫兼容的 NUMERIC 或 FLOAT
DoubleTypeHandler java.lang.Double, double 數(shù)據(jù)庫兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandler java.math.BigDecimal 數(shù)據(jù)庫兼容的 NUMERIC 或 DECIMAL
StringTypeHandler java.lang.String CHAR, VARCHAR
ClobTypeHandler java.lang.String CLOB, LONGVARCHAR
NStringTypeHandler java.lang.String NVARCHAR, NCHAR
NClobTypeHandler java.lang.String NCLOB
ByteArrayTypeHandler byte[] 數(shù)據(jù)庫兼容的字節(jié)流類型
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-任何兼容的字符串類型,存儲(chǔ)枚舉的名稱(而不是索引)
EnumOrdinalTypeHandler Enumeration Type 任何兼容的 NUMERIC 或 DOUBLE 類型,存儲(chǔ)枚舉的索引(而不是名稱)。
可以重寫類型處理器或創(chuàng)建自己的類型處理器來處理不支持的或非標(biāo)準(zhǔn)的類型。具體的做法為:實(shí)現(xiàn)
org.apache.ibatis.type.TypeHandler接口,或繼承一個(gè)很便利的類org.apache.ibatis.type.BaseTypeHandler,然后可以選擇性地將它映射到一個(gè)JDBC類型。比如:
//
ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR) public class ExampleTypeHandler 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);
}}
并且還需要在配置文件里面加上:
<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
使用這個(gè)的類型處理器將會(huì)覆蓋已經(jīng)存在的處理Java的String類型屬性和VARCHAR參數(shù)及結(jié)果的類型處理器。要注意MyBatis不會(huì)窺探數(shù)據(jù)庫元信息來決定使用哪種類型,所以必須在參數(shù)和結(jié)果映射中指明是VARCHAR類型字段,以使其能綁定到正確的類型處理器上。這是因?yàn)椋琈yBatis直到語句被執(zhí)行才清楚數(shù)據(jù)類型。
通過類型處理器的泛型,MyBatis可以得知該類型處理器的Java類型,不過這種行為可以通過兩種方法改變:
在類型處理器的元素(typeHandler element)上增加一個(gè)javaType屬性(比如,javaType="String");
在類型處理器的類上(TypeHandler class)增加一個(gè)@MappedTypes注解來指定與其關(guān)聯(lián)的Java類型列表。如果在javaType屬性中也同時(shí)制定,則注解方式將被忽略。
可以通過兩種方式來指定被關(guān)聯(lián)的JDBC類型:
在類型處理器的配置元素上增加一個(gè)javaType屬性(比如:javaType="VARCHAR");
在類型處理器的類上(TypeHandler class)增加一個(gè)@MappedJdbcTypes注解來指定與其關(guān)聯(lián)的JDBC類型列表。如果在javaType屬性中也同時(shí)指定,則注解方式將被忽略。
最后,還可以讓MyBatis查找類型處理器:
<!-- mybatis-config.xml -->
<typeHandlers>
<package name="org.mybatis.example"/>
</typeHandlers>
注意在使用自動(dòng)檢索(autodiscovery)功能的時(shí)候,只能通過注解的方式來指定JDBC類型。
你能創(chuàng)建一個(gè)泛型類型處理器,它可以處理多于一個(gè)類。為達(dá)到此目的,需要增加一個(gè)接收該類作為參數(shù)的構(gòu)造器,這樣在構(gòu)造一個(gè)類型處理器的時(shí)候MyBatis就會(huì)傳入一個(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;}
...
EnumTypeHandler和EnumOrdinalTypeHandler都是泛型處理器(generic TypeHandlers),接下來的部分詳細(xì)探討。
處理枚舉類型
若想映射枚舉類型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)持使用整形值代碼。那也一樣輕而易舉;在配置文件中把EnumOrdinalTypeHandler加到typeHandlers中即可,這樣每個(gè)RoundingMode將通過他們的序數(shù)值來映射成對(duì)應(yīng)的整形。
<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/>
</typeHandlers>
但是怎么樣能將同樣的Enum既映射成字符串又映射成整形呢?
自動(dòng)映射器(auto-mapper)會(huì)自動(dòng)選用EnumOrdinalTypeHandler來處理,所以如果我們想用普通的EnumTypeHandler,就非要為那些SQL語句顯示地設(shè)置要用到的類型處理器不可。
<!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語句強(qiáng)制使用resultMap來代替resultType。
對(duì)象工廠(objectFactory)
MyBatis每次創(chuàng)建結(jié)果對(duì)象的新實(shí)例時(shí),它都會(huì)使用一個(gè)對(duì)象工廠(ObjectFactory)實(shí)例來完成。默認(rèn)的對(duì)象工廠需要做的僅僅是實(shí)例化目標(biāo)類,要么通過默認(rèn)構(gòu)造方法,要么在參數(shù)映射存在的時(shí)候通過參數(shù)構(gòu)造方法來實(shí)例化。如果想覆蓋對(duì)象工廠的行為,則可以通過創(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<Class> constructorArgTypes, List<Object> constructorArgs)
{ return super.create(type, constructorArgTypes,
constructorArgs);
} public void setProperties(Properties properties)
{
super.setProperties(properties);
} public <T> boolean isCollection(Class<T> 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元素體內(nèi)定義的屬性會(huì)被傳遞給setProperties方法。
插件(plugins)
MyBatis允許你在已映射的語句執(zhí)行過程中的某一點(diǎn)進(jìn)行攔截調(diào)用。默認(rèn)情況下,Mybatis允許使用插件來攔截的方法調(diào)用包括:
Executor(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
ParameterHandler(getParameterObejct,setParameters)
ResultSetHandler(handlerResultSets,handlerOutputParameters)
StatementHandler(prepare,parameterize,batch,update,query)
這些類中方法的細(xì)節(jié)可以通過查看每個(gè)方法的簽名來發(fā)現(xiàn),或者直接查看MyBatis的發(fā)行包中的源代碼。假設(shè)你想做的不僅僅是方法的調(diào)用,那么你應(yīng)該很好的了解正在重寫的方法的行為。因?yàn)槿绻谝晥D修改或重寫已有方法的行為的時(shí)候,你很有可能在破壞MyBatis的核心模塊。這些都是更低層的類和方法,所以使用插件的時(shí)候要特別擔(dān)心。
通過MyBatis提供強(qiáng)大的機(jī)制,使用插件是非常簡(jiǎn)單的,只需要實(shí)現(xiàn)Interceptor接口,并指定想要攔截的方法簽名即可。
// ExamplePlugin.java
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args =
{MappedStatement.class,Object.class})}) public class ExamplePlugin implements Interceptor
{ public Object intercept(Invocation invocation) throws Throwable
{ return
invocation.proceed();
} public Object plugin(Object target)
{ return Plugin.wrap(target,
this);
} public void setProperties(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í)行底層映射語句的內(nèi)部對(duì)象。
覆蓋配置類
除了用插件來修改MyBatis核心行為之外,還可以通過完全覆蓋配置類來達(dá)到目的。只需繼承后覆蓋其中的每個(gè)方法,再把它傳遞到
sqlSessionFactoryBuilder.build(myConfig)方法即可。再次重申,這可能會(huì)嚴(yán)重影響Mybatis的行為,務(wù)請(qǐng)慎之又慎!
配置環(huán)境(environments)
MyBatis可以配置成適應(yīng)多種環(huán)境,這種機(jī)制有助于將sql映射應(yīng)用于多種數(shù)據(jù)庫中,現(xiàn)實(shí)情況下有多種理由需要這么做。例如,開發(fā)、測(cè)試和生產(chǎn)環(huán)境需要有不同的配置;或者共享相同的Schema的多個(gè)生產(chǎn)數(shù)據(jù)庫,想使用相同的sql映射。許多類似的用例。
盡管可以配置多個(gè)環(huán)境,但是每個(gè)SqlSessionFactory實(shí)例只能選擇其一。
所以,如果想連接兩個(gè)數(shù)據(jù)庫,就需要?jiǎng)?chuàng)建兩個(gè)SqlSessionFactory實(shí)例,每個(gè)數(shù)據(jù)庫對(duì)應(yīng)一個(gè)。而如果是三個(gè)數(shù)據(jù)庫,就需要三個(gè)實(shí)例,依此類推。
每個(gè)數(shù)據(jù)庫對(duì)應(yīng)一個(gè)SqlSessionFactory實(shí)例
為了指定創(chuàng)建哪種環(huán)境,只要將它作為可選參數(shù)傳遞給SqlSessionFactoryBuilder即可??梢越邮墉h(huán)境配置的兩個(gè)方法簽名是:
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment,properties);
如果忽略了環(huán)境參數(shù),那么默認(rèn)環(huán)境將會(huì)被加載,如下所示:
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader,properties);
環(huán)境元素定義了如何配置環(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是一目了然的。隨你怎么命名,只要保證默認(rèn)環(huán)境要匹配其中一個(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)閉連接,然而一些容器并不希望這樣,因此需要將closeConnection屬性設(shè)置為false來阻止它默認(rèn)的行為。例如
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
如果正在使用Spring+MyBatis,則沒有必要配置事務(wù)管理器,因?yàn)镾pring模塊會(huì)使用自帶的管理器來覆蓋前面的配置。
這兩種事務(wù)管理器類型都不需要任何屬性。它們只不過是類型別名,換句話說,你可以使用TransactionFactory接口的實(shí)現(xiàn)類的完全限定名或類型別名替代它們。
public interface TransactionFactory{
void setProperties(Properties props);
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
任何在xml中配置的屬性在實(shí)例化之后將會(huì)被傳遞給setProperties方法。你也需要?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;
}
使用這兩個(gè)接口,完全可以自定義MyBatis對(duì)事務(wù)的處理。
數(shù)據(jù)源(dataSource)
dataSource元素使用了標(biāo)準(zhǔn)的JDBC數(shù)據(jù)源接口來配置JDBC連接對(duì)象的資源。
許多MyBatis的應(yīng)用程序?qū)?huì)按示例中的例子來配置數(shù)據(jù)源。然而它并不是必須的。要知道為了方便使用延遲加載,數(shù)據(jù)源才是必須的。
有三種內(nèi)建的數(shù)據(jù)源類型(也就是 type="[]UNPOOLED|POOLED|JNDI]");
UNPOOLED --- 這個(gè)數(shù)據(jù)源的實(shí)現(xiàn)只是被請(qǐng)求時(shí)打開和關(guān)閉連接。雖然有一點(diǎn)慢,它對(duì)在及時(shí)可用連接方面沒有性能要求的簡(jiǎn)單應(yīng)用是一個(gè)很好的選擇。不同的數(shù)據(jù)庫在這方面表現(xiàn)也是不一樣的,所以對(duì)某些數(shù)據(jù)庫來說使用連接池并不重要,這個(gè)配置也是理想。UNPOOLED類型的數(shù)據(jù)源僅僅需要配置以下5種屬性:
driver -- 這是JDBC驅(qū)動(dòng)的Java類的完全限定名(并不是JDBC驅(qū)動(dòng)中可能包含的數(shù)據(jù)源類)
url -- 這是數(shù)據(jù)庫的JDBC URL 地址。
username -- 登錄數(shù)據(jù)庫的用戶名。
password -- 登錄數(shù)據(jù)庫的密碼。
defaultTransactionIsolationLevel -- 默認(rèn)的連接事務(wù)隔離級(jí)別。
作為可選項(xiàng),可以傳遞屬性給數(shù)據(jù)庫驅(qū)動(dòng)。要這樣做,屬性的前綴為"driver.",例如:
driver.encoding=UTF-8
這將通過DriverManager,getConnection(url,driverProperties)方法傳遞值為UTF-8的encoding屬性給數(shù)據(jù)庫驅(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下的屬性外,會(huì)有更多屬性用來配置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秒)。
poolPingQuery -- 發(fā)送到數(shù)據(jù)庫的偵測(cè)查詢,用來檢驗(yàn)連接是否處在正常的工作秩序中,并且準(zhǔn)備接受請(qǐng)求。默認(rèn)是"NOT PING QUERY SET",這會(huì)導(dǎo)致多數(shù)數(shù)據(jù)庫連接失敗時(shí)帶有一個(gè)恰當(dāng)?shù)腻e(cuò)誤信息。
poolPingEnabled -- 是否啟用偵測(cè)。若開啟,也必須使用一個(gè)可執(zhí)行的SQL語句設(shè)置poolPingQuery屬性(最好是一個(gè)非??斓腟QL),默認(rèn)值:false。
poolPingConnectionsNotUsedFor -- 配置poolPingQuery使用的頻度。這可以被設(shè)置成匹配具體的數(shù)據(jù)庫連接超時(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ù)源配置只要兩個(gè)屬性:
initial_context -- 這個(gè)屬性用來在InitialContext中尋找上下文(即,initialContext.lookup(initial_context))。這是個(gè)可選屬性,如果忽略,那么data_source屬性將會(huì)直接從InitialContext中尋找。
data_source -- 這是引用數(shù)據(jù)源實(shí)例位置的上下文的路徑。提供了 initial_context配置時(shí)會(huì)在其返回的上下文中進(jìn)行查找,沒有提供時(shí)則直接在InitialContext中查找。
和其他數(shù)據(jù)源配置類似,可以通過添加前綴"env."直接把屬性傳遞給初始上下文。比如:
env.encoding=UTF-8
這會(huì)在初始上下文(InitialContext)實(shí)例化時(shí)往它的構(gòu)造方法傳遞值為UTF-8的encoding屬性。
通過需要實(shí)現(xiàn)接口
org.apache.ibatis.datasource.DataSourceFactory,也可使用任何第三方數(shù)據(jù)源,:
public interface DataSourceFactory{
void setProperties(Properties props);
DataSource getDataSource();
}
org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory 可被用作父類來構(gòu)建新的數(shù)據(jù)源適配器,比如下面這段插入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();}
}
為了令其工作,為每個(gè)需要MyBatis調(diào)用的setter方法中增加一個(gè)屬性。下面是一個(gè)可以連接到PostgreSQL數(shù)據(jù)庫的例子:
<dataSource type="org.myproject.C3P0DataSourceFactory">
<property name="driver" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql:mydb"/>
<property name="username" value="postgres"/>
<property name="password" value="root"/>
</dataSource>
databaseIdProvider
MyBatis可以根據(jù)不同的數(shù)據(jù)庫廠商執(zhí)行不同的語句,這種多廠商的支持是基于映射語句中的databaseId屬性。MyBatis會(huì)加載不帶databaseId屬性和帶有匹配當(dāng)前數(shù)據(jù)庫databaseId屬性的所有語句。如果同時(shí)找到帶有databaseId和不帶databaseId的相同語句,則后者被舍棄。為支持多廠商特性,只要像下面這樣在mybatis-config.xml文件中加入databaseIdProvider即可:
<databaseIdProvider type="DB_VENDOR" />
這里的DB_VENDOR會(huì)通過DatabaseMetaData#getDatabaseProductName()返回的字符串進(jìn)行設(shè)置。由于通常情況下這個(gè)字符串都非常長(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>
在有properties時(shí),DB_VENDOR databaseIdProvider的將被設(shè)置為第一個(gè)能匹配數(shù)據(jù)庫產(chǎn)品名稱的屬性鍵值對(duì)應(yīng)的值,如果沒有匹配的屬性將會(huì)設(shè)置為”null“。在這個(gè)例子中,如果getDatabaseProductName()返回”O(jiān)racle(DataDirect)“,databaseId將被設(shè)置為"oracle"。
可以通過實(shí)現(xiàn)接口
org.apache.ibatis.mapping.DatabaseIdProvider并在mybatis-config.xml中注冊(cè)來構(gòu)建自己的DatabaseIdProvider:
public interface DatabaseIdProvider{
void setProperties(Properties p);
String getDatabaseId(DataSource dataSource) throws SQLException;
}
映射器(mappers)
既然MyBatis的行為已經(jīng)由上述元素配置完了,現(xiàn)在就要定義SQL映射語句了。但是首先需要告訴MyBatis到哪里去找到這些語句。Java在自動(dòng)查找這方面沒有提供一個(gè)很好的方法,所以最佳的方式是告訴MyBatis到哪里去找映射文件。可以使用相對(duì)于類路徑的資源引用、或完全限定資源定位符(包括file:///的URL),或類名和包名等等。例如:
<!-- Using classpath relative resources -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- Using url fully qualified paths -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- Using mapper interface classes -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- Register all interfaces in a package as mappers -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
這些配置會(huì)告訴了MyBatis去哪里找映射文件,剩下的細(xì)節(jié)就應(yīng)該是每個(gè)SQL映射文件了。