1.概述
MyBatis 是支持定制化SQL、存儲(chǔ)過程以及高級(jí)映射的優(yōu)秀的持久層框架摧阅。MyBatis 避免了幾乎所有的 JDBC 代碼和手動(dòng)設(shè)置參數(shù)以及獲取結(jié)果集香璃。MyBatis 可以對(duì)配置和原生Map使用簡(jiǎn)單的 XML 或注解虱咧,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對(duì)象)映射成數(shù)據(jù)庫中的記錄叉庐。
安裝
使用 Maven 來構(gòu)建項(xiàng)目,則需將下面的 dependency 代碼置于 pom.xml 文件中:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
功能架構(gòu)
1)API接口層:提供給外部使用的接口API儿惫,開發(fā)人員通過這些本地API來操縱數(shù)據(jù)庫。接口層一接收到調(diào)用請(qǐng)求就會(huì)調(diào)用數(shù)據(jù)處理層來完成具體的數(shù)據(jù)處理伸但。
2)數(shù)據(jù)處理層:負(fù)責(zé)具體的SQL查找肾请、SQL解析、SQL執(zhí)行和執(zhí)行結(jié)果映射處理等更胖。它主要的目的是根據(jù)調(diào)用的請(qǐng)求完成一次數(shù)據(jù)庫操作铛铁。
3)基礎(chǔ)支撐層:負(fù)責(zé)最基礎(chǔ)的功能支撐,包括連接管理函喉、事務(wù)管理避归、配置加載和緩存處理,這些都是共用的東西管呵,將他們抽取出來作為最基礎(chǔ)的組件梳毙。為上層的數(shù)據(jù)處理層提供最基礎(chǔ)的支撐。優(yōu)點(diǎn)
1)簡(jiǎn)單易學(xué):本身就很小且簡(jiǎn)單捐下。沒有任何第三方依賴账锹,最簡(jiǎn)單安裝只要兩個(gè)jar文件+配置幾個(gè)sql映射文件易于學(xué)習(xí)萌业,易于使用,通過文檔和源代碼奸柬,可以比較完全的掌握它的設(shè)計(jì)思路和實(shí)現(xiàn)生年。
2)靈活:mybatis不會(huì)對(duì)應(yīng)用程序或者數(shù)據(jù)庫的現(xiàn)有設(shè)計(jì)強(qiáng)加任何影響。 sql寫在xml里廓奕,便于統(tǒng)一管理和優(yōu)化抱婉。通過sql基本上可以實(shí)現(xiàn)我們不使用數(shù)據(jù)訪問框架可以實(shí)現(xiàn)的所有功能,或許更多桌粉。
3)解除sql與程序代碼的耦合:通過提供DAL層蒸绩,將業(yè)務(wù)邏輯和數(shù)據(jù)訪問邏輯分離,使系統(tǒng)的設(shè)計(jì)更清晰铃肯,更易維護(hù)患亿,更易單元測(cè)試。sql和代碼的分離押逼,提高了可維護(hù)性步藕。
4)提供映射標(biāo)簽,支持對(duì)象與數(shù)據(jù)庫的orm字段關(guān)系映射
5)提供對(duì)象關(guān)系映射標(biāo)簽挑格,支持對(duì)象關(guān)系組建維護(hù)
6)提供xml標(biāo)簽咙冗,支持編寫動(dòng)態(tài)sql。缺點(diǎn)
1)編寫SQL語句時(shí)工作量很大恕齐,尤其是字段多乞娄、關(guān)聯(lián)表多時(shí),更是如此显歧。
2)SQL語句依賴于數(shù)據(jù)庫仪或,導(dǎo)致數(shù)據(jù)庫移植性差,不能更換數(shù)據(jù)庫士骤。
3)框架還是比較簡(jiǎn)陋范删,功能尚有缺失,雖然簡(jiǎn)化了數(shù)據(jù)綁定代碼拷肌,但是整個(gè)底層數(shù)據(jù)庫查詢實(shí)際還是要自己寫的到旦,工作量也比較大,而且不太容易適應(yīng)快速數(shù)據(jù)庫修改巨缘。
4)二級(jí)緩存機(jī)制不佳
2.XML配置
- 包含設(shè)置信息(settings)和屬性信息(properties)
2.1 properties
- 這些屬性都是可外部配置且可動(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)配置的屬性值搁骑。比如:
<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);
如果屬性在不只一個(gè)地方進(jìn)行了配置,那么 MyBatis 將按照下面的順序來加載:
1)在 properties 元素體內(nèi)指定的屬性首先被讀取乏冀。
2)然后根據(jù) properties 元素中的 resource 屬性讀取類路徑下屬性文件或根據(jù) url 屬性指定的路徑讀取屬性文件蝶糯,并覆蓋已讀取的同名屬性。
3)最后讀取作為方法參數(shù)傳遞的屬性辆沦,并覆蓋已讀取的同名屬性昼捍。
因此,通過方法參數(shù)傳遞的屬性具有最高優(yōu)先級(jí)众辨,resource/url 屬性中指定的配置文件次之端三,最低優(yōu)先級(jí)的是 properties 屬性中指定的屬性
2.2 settings
-
會(huì)改變 MyBatis 的運(yùn)行時(shí)行為
<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="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>
2.3 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ì)在包名下面搜索需要的 Java Bean育拨,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
每一個(gè)在包 domain.blog 中的 Java Bean谨履,在沒有注解的情況下,會(huì)使用 Bean 的首字母小寫的非限定類名來作為它的別名熬丧。 比如 domain.blog.Author 的別名為 author笋粟;若有注解,則別名為其注解值析蝴『Σ叮看下面的例子:
@Alias("author")
public class Author {
...
}
已經(jīng)為許多常見的 Java 類型內(nèi)建了相應(yīng)的類型別名。它們都是大小寫不敏感的闷畸,需要注意的是由基本類型名稱重復(fù)導(dǎo)致的特殊處理尝盼。
2.4 typeHandlers
-
無論是 MyBatis 在預(yù)處理語句(PreparedStatement)中設(shè)置一個(gè)參數(shù)時(shí),還是從結(jié)果集中取出一個(gè)值時(shí)佑菩, 都會(huì)用類型處理器將獲取的值以合適的方式轉(zhuǎn)換成 Java 類型盾沫。下表描述了一些默認(rèn)的類型處理器。
- 你可以重寫類型處理器或創(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 {
@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 類型琼富,不過這種行為可以通過兩種方法改變:
1)在類型處理器的配置元素(typeHandler element)上增加一個(gè) javaType 屬性(比如:javaType="String")仪吧;
2)在類型處理器的類上(TypeHandler class)增加一個(gè) @MappedTypes 注解來指定與其關(guān)聯(lián)的 Java 類型列表。 如果在 javaType 屬性中也同時(shí)指定鞠眉,則注解方式將被忽略薯鼠。
可以通過兩種方式來指定被關(guān)聯(lián)的 JDBC 類型:
1)在類型處理器的配置元素上增加一個(gè) javaType 屬性(比如:javaType="VARCHAR");
2)在類型處理器的類上(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 extends BaseTypeHandler {
private Class type;
public GenericTypeHandler(Class type) {
if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
this.type = type;
}
...
EnumTypeHandler 和 EnumOrdinalTypeHandler 都是泛型類型處理器(generic TypeHandlers)
2.5 處理枚舉類型
- 若想映射枚舉類型 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。
2.6 對(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ì)象工廠的默認(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 方法。
2.7 插件(plugins)
MyBatis 允許你在已映射語句執(zhí)行過程中的某一點(diǎn)進(jìn)行攔截調(diào)用舆乔。默認(rèn)情況下和措,MyBatis 允許使用插件來攔截的方法調(diào)用包括:
1)Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
2)ParameterHandler (getParameterObject, setParameters)
3)ResultSetHandler (handleResultSets, handleOutputParameters)
4)StatementHandler (prepare, parameterize, batch, update, query)
這些類中方法的細(xì)節(jié)可以通過查看每個(gè)方法的簽名來發(fā)現(xiàn),或者直接查看 MyBatis 的發(fā)行包中的源代碼蜕煌。 假設(shè)你想做的不僅僅是監(jiān)控方法的調(diào)用,那么你應(yīng)該很好的了解正在重寫的方法的行為诬留。 因?yàn)槿绻谠噲D修改或重寫已有方法的行為的時(shí)候斜纪,你很可能在破壞 MyBatis 的核心模塊。 這些都是更低層的類和方法文兑,所以使用插件的時(shí)候要特別當(dāng)心盒刚。通過 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ì)象涡上。
- NOTE 覆蓋配置類
除了用插件來修改 MyBatis 核心行為之外,還可以通過完全覆蓋配置類來達(dá)到目的拒名。只需繼承后覆蓋其中的每個(gè)方法吩愧,再把它傳遞到 sqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申增显,這可能會(huì)嚴(yán)重影響 MyBatis 的行為雁佳,務(wù)請(qǐng)慎之又慎。
2.8 配置環(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í)例寝蹈,依此類推,記起來很簡(jiǎn)單:
每個(gè)數(shù)據(jù)庫對(duì)應(yīng)一個(gè) SqlSessionFactory 實(shí)例為了指定創(chuàng)建哪種環(huán)境登淘,只要將它作為可選的參數(shù)傳遞給 SqlSessionFactoryBuilder 即可箫老。可以接受環(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):
1)默認(rèn)的環(huán)境 ID(比如:default="development")流妻。
2)每個(gè) environment 元素定義的環(huán)境 ID(比如:id="development")牲蜀。
3)事務(wù)管理器的配置(比如:type="JDBC")。
4)數(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]"):
1)JDBC – 這個(gè)配置就是直接使用了 JDBC 的提交和回滾設(shè)置度苔,它依賴于從數(shù)據(jù)源得到的連接來管理事務(wù)范圍。
2)MANAGED – 這個(gè)配置幾乎沒做什么棕叫。它從來不提交或回滾一個(gè)連接林螃,而是讓容器來管理事務(wù)的整個(gè)生命周期(比如 JEE 應(yīng)用服務(wù)器的上下文)。 默認(rèn)情況下它會(huì)關(guān)閉連接俺泣,然而一些容器并不希望這樣疗认,因此需要將 closeConnection 屬性設(shè)置為 false 來阻止它默認(rèn)的關(guān)閉行為完残。例如:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
NOTE : 如果你正在使用 Spring + MyBatis,則沒有必要配置事務(wù)管理器横漏, 因?yàn)?Spring 模塊會(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ì)象的資源。
1)許多 MyBatis 的應(yīng)用程序?qū)?huì)按示例中的例子來配置數(shù)據(jù)源型将。然而它并不是必須的覆醇。要知道為了方便使用延遲加載玛痊,數(shù)據(jù)源才是必須的赴恨。有三種內(nèi)建的數(shù)據(jù)源類型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
1)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 種屬性:
1-1)driver – 這是 JDBC 驅(qū)動(dòng)的 Java 類的完全限定名(并不是JDBC驅(qū)動(dòng)中可能包含的數(shù)據(jù)源類)。
1-2)url – 這是數(shù)據(jù)庫的 JDBC URL 地址御铃。
1-3)username – 登錄數(shù)據(jù)庫的用戶名俊鱼。
1-4)password – 登錄數(shù)據(jù)庫的密碼。
1-4)defaultTransactionIsolationLevel – 默認(rèn)的連接事務(wù)隔離級(jí)別畅买。
作為可選項(xiàng),你也可以傳遞屬性給數(shù)據(jù)庫驅(qū)動(dòng)细睡。要這樣做谷羞,屬性的前綴為"driver.",例如:driver.encoding=UTF8
這將通過DriverManager.getConnection(url,driverProperties)方法傳遞值為 UTF8 的 encoding 屬性給數(shù)據(jù)庫驅(qū)動(dòng)溜徙。
2)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ù)源:
2-1)poolMaximumActiveConnections – 在任意時(shí)間可以存在的活動(dòng)(也就是正在使用)連接數(shù)量,默認(rèn)值:10
2-2)poolMaximumIdleConnections – 任意時(shí)間可能存在的空閑連接數(shù)图贸。
2-3)poolMaximumCheckoutTime – 在被強(qiáng)制返回之前蹂季,池中連接被檢出(checked out)時(shí)間冕广,默認(rèn)值:20000 毫秒(即 20 秒)
2-4)poolTimeToWait – 這是一個(gè)底層設(shè)置,如果獲取連接花費(fèi)的相當(dāng)長的時(shí)間偿洁,它會(huì)給連接池打印狀態(tài)日志并重新嘗試獲取一個(gè)連接(避免在誤配置的情況下一直安靜的失斎龊骸),默認(rèn)值:20000 毫秒(即 20 秒)涕滋。
2-5)poolPingQuery – 發(fā)送到數(shù)據(jù)庫的偵測(cè)查詢睬辐,用來檢驗(yàn)連接是否處在正常工作秩序中并準(zhǔn)備接受請(qǐng)求。默認(rèn)是"NO PING QUERY SET"宾肺,這會(huì)導(dǎo)致多數(shù)數(shù)據(jù)庫驅(qū)動(dòng)失敗時(shí)帶有一個(gè)恰當(dāng)?shù)腻e(cuò)誤消息溯饵。
2-6)poolPingEnabled – 是否啟用偵測(cè)查詢。若開啟锨用,也必須使用一個(gè)可執(zhí)行的 SQL 語句設(shè)置 poolPingQuery 屬性(最好是一個(gè)非撤峥快的 SQL),默認(rèn)值:false黔酥。
2-7)poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的使用頻度藻三。這可以被設(shè)置成匹配具體的數(shù)據(jù)庫連接超時(shí)時(shí)間,來避免不必要的偵測(cè)跪者,默認(rèn)值:0(即所有連接每一時(shí)刻都被偵測(cè) — 當(dāng)然僅當(dāng) poolPingEnabled 為 true 時(shí)適用)棵帽。
3)JNDI– 這個(gè)數(shù)據(jù)源的實(shí)現(xiàn)是為了能在如 EJB 或應(yīng)用服務(wù)器這類容器中使用,容器可以集中或在外部配置數(shù)據(jù)源渣玲,然后放置一個(gè) JNDI 上下文的引用逗概。這種數(shù)據(jù)源配置只需要兩個(gè)屬性:
3-1)initial_context – 這個(gè)屬性用來在 InitialContext 中尋找上下文(即,initialContext.lookup(initial_context))忘衍。這是個(gè)可選屬性逾苫,如果忽略,那么 data_source 屬性將會(huì)直接從 InitialContext 中尋找枚钓。
3-2)data_source – 這是引用數(shù)據(jù)源實(shí)例位置的上下文的路徑铅搓。提供了 initial_context 配置時(shí)會(huì)在其返回的上下文中進(jìn)行查找,沒有提供時(shí)則直接在 InitialContext 中查找搀捷。
和其他數(shù)據(jù)源配置類似星掰,可以通過添加前綴"env."直接把屬性傳遞給初始上下文。比如:env.encoding=UTF8
這就會(huì)在初始上下文(InitialContext)實(shí)例化時(shí)往它的構(gòu)造方法傳遞值為 UTF8 的 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 數(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 方法中增加一個(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>
2.9 databaseIdProvider
MyBatis 可以根據(jù)不同的數(shù)據(jù)庫廠商執(zhí)行不同的語句饭于,這種多廠商的支持是基于映射語句中的 databaseId 屬性蜀踏。 MyBatis 會(huì)加載不帶 databaseId 屬性和帶有匹配當(dāng)前數(shù)據(jù)庫 databaseId 屬性的所有語句维蒙。 如果同時(shí)找到帶有 databaseId 和不帶 databaseId 的相同語句,則后者會(huì)被舍棄脓斩。 為支持多廠商特性只要像下面這樣在 mybatis-config.xml 文件中加入 databaseIdProvider 即可:
<databaseIdProvider type="DB_VENDOR" />
這里的 DB_VENDOR 會(huì)通過 DatabaseMetaData#getDatabaseProductName() 返回的字符串進(jìn)行設(shè)置木西。 由于通常情況下這個(gè)字符串都非常長而且相同產(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() 返回"Oracle (DataDirect)",databaseId 將被設(shè)置為"oracle"重绷。
你可以通過實(shí)現(xiàn)接口
public interface DatabaseIdProvider {
void setProperties(Properties p);
String getDatabaseId(DataSource dataSource) throws SQLException;
}
2.10 映射器(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 映射文件了伙菊,也就是接下來我們要討論的。