要使用 MyBatis, 只需將 mybatis-x.x.x.jar 文件置于類路徑(classpath)中即可。
如果使用 Maven 來(lái)構(gòu)建項(xiàng)目,則需將下面的依賴代碼置于 pom.xml 文件中:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
每個(gè)基于 MyBatis 的應(yīng)用都是以一個(gè) SqlSessionFactory 的實(shí)例為核心的。SqlSessionFactory 的實(shí)例可以通過(guò) SqlSessionFactoryBuilder 獲得。而 SqlSessionFactoryBuilder 則可以從 XML 配置文件或一個(gè)預(yù)先配置的 Configuration 實(shí)例來(lái)構(gòu)建出 SqlSessionFactory 實(shí)例。
從 XML 文件中構(gòu)建 SqlSessionFactory 的實(shí)例非常簡(jiǎn)單,建議使用類路徑下的資源文件進(jìn)行配置。 但也可以使用任意的輸入流(InputStream)實(shí)例,比如用文件路徑字符串或 file:// URL
構(gòu)造的輸入流。MyBatis 包含一個(gè)名叫 Resources 的工具類,它包含一些實(shí)用方法,使得從類路徑或其它位置加載資源文件更加容易。
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
XML 配置文件中包含了對(duì) MyBatis 系統(tǒng)的核心設(shè)置,包括獲取數(shù)據(jù)庫(kù)連接實(shí)例的數(shù)據(jù)源(DataSource)以及決定事務(wù)作用域和控制方式的事務(wù)管理器(TransactionManager)。后面會(huì)再探討 XML 配置文件的詳細(xì)內(nèi)容,這里先給出一個(gè)簡(jiǎn)單的示例:
<?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>
<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>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
當(dāng)然,還有很多可以在 XML 文件中配置的選項(xiàng),上面的示例僅羅列了最關(guān)鍵的部分。 注意 XML 頭部的聲明,它用來(lái)驗(yàn)證 XML 文檔的正確性。environment 元素體中包含了事務(wù)管理和連接池的配置。mappers 元素則包含了一組映射器(mapper),這些映射器的 XML 映射文件包含了 SQL 代碼和映射定義信息。
如果你更愿意直接從 Java 代碼而不是 XML 文件中創(chuàng)建配置,或者想要?jiǎng)?chuàng)建你自己的配置建造器,MyBatis 也提供了完整的配置類,提供了所有與 XML 文件等價(jià)的配置項(xiàng)。
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class
);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
注意該例中,configuration 添加了一個(gè)映射器類(mapper class)。映射器類是 Java 類,它們包含 SQL 映射注解從而避免依賴 XML 文件。不過(guò),由于 Java 注解的一些限制以及某些 MyBatis 映射的復(fù)雜性,要使用大多數(shù)高級(jí)映射(比如:嵌套聯(lián)合映射),仍然需要使用
XML 配置。有鑒于此,如果存在一個(gè)同名 XML 配置文件,MyBatis 會(huì)自動(dòng)查找并加載它(在這個(gè)例子中,基于類路徑和 BlogMapper.class
的類名,會(huì)加載 BlogMapper.xml
)。具體細(xì)節(jié)稍后討論。
既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的實(shí)例。SqlSession 提供了在數(shù)據(jù)庫(kù)執(zhí)行 SQL 命令所需的所有方法。你可以通過(guò) SqlSession 實(shí)例來(lái)直接執(zhí)行已映射的 SQL 語(yǔ)句。例如:
try (SqlSession session = sqlSessionFactory.openSession()) {
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}
誠(chéng)然,這種方式能夠正常工作,對(duì)使用舊版本 MyBatis 的用戶來(lái)說(shuō)也比較熟悉。但現(xiàn)在有了一種更簡(jiǎn)潔的方式——使用和指定語(yǔ)句的參數(shù)和返回值相匹配的接口(比如 BlogMapper.class
),現(xiàn)在你的代碼不僅更清晰,更加類型安全,還不用擔(dān)心可能出錯(cuò)的字符串字面值以及強(qiáng)制類型轉(zhuǎn)換。
例如:
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class
);
Blog blog = mapper.selectBlog(101);
}
現(xiàn)在我們來(lái)探究一下這段代碼究竟做了些什么。
現(xiàn)在你可能很想知道 SqlSession 和 Mapper 到底具體執(zhí)行了些什么操作,但 SQL 語(yǔ)句映射是個(gè)相當(dāng)廣泛的話題,可能會(huì)占去文檔的大部分篇幅。 但為了讓你能夠了解個(gè)大概,這里會(huì)給出幾個(gè)例子。
在上面提到的例子中,一個(gè)語(yǔ)句既可以通過(guò) XML 定義,也可以通過(guò)注解定義。我們先看看 XML 定義語(yǔ)句的方式,事實(shí)上 MyBatis 提供的所有特性都可以利用基于 XML 的映射語(yǔ)言來(lái)實(shí)現(xiàn),這使得 MyBatis 在過(guò)去的數(shù)年間得以流行。如果你用過(guò)舊版本的 MyBatis,你應(yīng)該對(duì)這個(gè)概念比較熟悉。 但相比于之前的版本,新版本改進(jìn)了許多 XML 的配置,后面我們會(huì)提到這些改進(jìn)。這里給出一個(gè)基于 XML 映射語(yǔ)句的示例,它應(yīng)該可以滿足上個(gè)示例中 SqlSession 的調(diào)用。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
為了這個(gè)簡(jiǎn)單的例子,我們似乎寫了不少配置,但其實(shí)并不多。在一個(gè) XML 映射文件中,可以定義無(wú)數(shù)個(gè)映射語(yǔ)句,這樣一來(lái),XML 頭部和文檔類型聲明部分就顯得微不足道了。文檔的其它部分很直白,容易理解。 它在命名空間 “org.mybatis.example.BlogMapper” 中定義了一個(gè)名為 “selectBlog” 的映射語(yǔ)句,這樣你就可以用全限定名 “org.mybatis.example.BlogMapper.selectBlog” 來(lái)調(diào)用映射語(yǔ)句了,就像上面例子中那樣:
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
你可能會(huì)注意到,這種方式和用全限定名調(diào)用 Java 對(duì)象的方法類似。這樣,該命名就可以直接映射到在命名空間中同名的映射器類,并將已映射的 select 語(yǔ)句匹配到對(duì)應(yīng)名稱、參數(shù)和返回類型的方法。因此你就可以像上面那樣,不費(fèi)吹灰之力地在對(duì)應(yīng)的映射器接口調(diào)用方法,就像下面這樣:
BlogMapper mapper = session.getMapper(BlogMapper.class
);
Blog blog = mapper.selectBlog(101);
第二種方法有很多優(yōu)勢(shì),首先它不依賴于字符串字面值,會(huì)更安全一點(diǎn);其次,如果你的 IDE 有代碼補(bǔ)全功能,那么代碼補(bǔ)全可以幫你快速選擇到映射好的 SQL 語(yǔ)句。
提示 對(duì)命名空間的一點(diǎn)補(bǔ)充
在之前版本的 MyBatis 中,命名空間(Namespaces)的作用并不大,是可選的。 但現(xiàn)在,隨著命名空間越發(fā)重要,你必須指定命名空間。
命名空間的作用有兩個(gè),一個(gè)是利用更長(zhǎng)的全限定名來(lái)將不同的語(yǔ)句隔離開(kāi)來(lái),同時(shí)也實(shí)現(xiàn)了你上面見(jiàn)到的接口綁定。就算你覺(jué)得暫時(shí)用不到接口綁定,你也應(yīng)該遵循這里的規(guī)定,以防哪天你改變了主意。 長(zhǎng)遠(yuǎn)來(lái)看,只要將命名空間置于合適的 Java 包命名空間之中,你的代碼會(huì)變得更加整潔,也有利于你更方便地使用 MyBatis。
命名解析:為了減少輸入量,MyBatis 對(duì)所有具有名稱的配置元素(包括語(yǔ)句,結(jié)果映射,緩存等)使用了如下的命名解析規(guī)則。
對(duì)于像 BlogMapper 這樣的映射器類來(lái)說(shuō),還有另一種方法來(lái)完成語(yǔ)句映射。 它們映射的語(yǔ)句可以不用 XML 來(lái)配置,而可以使用 Java 注解來(lái)配置。比如,上面的 XML 示例可以被替換成如下的配置:
package org.mybatis.example;
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
使用注解來(lái)映射簡(jiǎn)單語(yǔ)句會(huì)使代碼顯得更加簡(jiǎn)潔,但對(duì)于稍微復(fù)雜一點(diǎn)的語(yǔ)句,Java 注解不僅力不從心,還會(huì)讓你本就復(fù)雜的 SQL 語(yǔ)句更加混亂不堪。 因此,如果你需要做一些很復(fù)雜的操作,最好用 XML 來(lái)映射語(yǔ)句。
選擇何種方式來(lái)配置映射,以及認(rèn)為是否應(yīng)該要統(tǒng)一映射語(yǔ)句定義的形式,完全取決于你和你的團(tuán)隊(duì)。 換句話說(shuō),永遠(yuǎn)不要拘泥于一種方式,你可以很輕松的在基于注解和 XML 的語(yǔ)句映射方式間自由移植和切換。
理解我們之前討論過(guò)的不同作用域和生命周期類別是至關(guān)重要的,因?yàn)殄e(cuò)誤的使用會(huì)導(dǎo)致非常嚴(yán)重的并發(fā)問(wèn)題。
提示 對(duì)象生命周期和依賴注入框架
依賴注入框架可以創(chuàng)建線程安全的、基于事務(wù)的 SqlSession 和映射器,并將它們直接注入到你的 bean 中,因此可以直接忽略它們的生命周期。 如果對(duì)如何通過(guò)依賴注入框架使用 MyBatis 感興趣,可以研究一下 MyBatis-Spring 或 MyBatis-Guice 兩個(gè)子項(xiàng)目。
這個(gè)類可以被實(shí)例化、使用和丟棄,一旦創(chuàng)建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 實(shí)例的最佳作用域是方法作用域(也就是局部方法變量)。 你可以重用 SqlSessionFactoryBuilder 來(lái)創(chuàng)建多個(gè) SqlSessionFactory 實(shí)例,但最好還是不要一直保留著它,以保證所有的 XML 解析資源可以被釋放給更重要的事情。
SqlSessionFactory 一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運(yùn)行期間一直存在,沒(méi)有任何理由丟棄它或重新創(chuàng)建另一個(gè)實(shí)例。 使用 SqlSessionFactory 的最佳實(shí)踐是在應(yīng)用運(yùn)行期間不要重復(fù)創(chuàng)建多次,多次重建 SqlSessionFactory 被視為一種代碼“壞習(xí)慣”。因此 SqlSessionFactory 的最佳作用域是應(yīng)用作用域。 有很多方法可以做到,最簡(jiǎn)單的就是使用單例模式或者靜態(tài)單例模式。
每個(gè)線程都應(yīng)該有它自己的 SqlSession 實(shí)例。SqlSession 的實(shí)例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請(qǐng)求或方法作用域。 絕對(duì)不能將 SqlSession 實(shí)例的引用放在一個(gè)類的靜態(tài)域,甚至一個(gè)類的實(shí)例變量也不行。 也絕不能將 SqlSession 實(shí)例的引用放在任何類型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你現(xiàn)在正在使用一種 Web 框架,考慮將 SqlSession 放在一個(gè)和 HTTP 請(qǐng)求相似的作用域中。 換句話說(shuō),每次收到 HTTP 請(qǐng)求,就可以打開(kāi)一個(gè) SqlSession,返回一個(gè)響應(yīng)后,就關(guān)閉它。 這個(gè)關(guān)閉操作很重要,為了確保每次都能執(zhí)行關(guān)閉操作,你應(yīng)該把這個(gè)關(guān)閉操作放到 finally 塊中。 下面的示例就是一個(gè)確保 SqlSession 關(guān)閉的標(biāo)準(zhǔn)模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的應(yīng)用邏輯代碼
}
在所有代碼中都遵循這種使用模式,可以保證所有數(shù)據(jù)庫(kù)資源都能被正確地關(guān)閉。
映射器是一些綁定映射語(yǔ)句的接口。映射器接口的實(shí)例是從 SqlSession 中獲得的。雖然從技術(shù)層面上來(lái)講,任何映射器實(shí)例的最大作用域與請(qǐng)求它們的 SqlSession 相同。但方法作用域才是映射器實(shí)例的最合適的作用域。 也就是說(shuō),映射器實(shí)例應(yīng)該在調(diào)用它們的方法中被獲取,使用完畢之后即可丟棄。 映射器實(shí)例并不需要被顯式地關(guān)閉。盡管在整個(gè)請(qǐng)求作用域保留映射器實(shí)例不會(huì)有什么問(wèn)題,但是你很快會(huì)發(fā)現(xiàn),在這個(gè)作用域上管理太多像 SqlSession 的資源會(huì)讓你忙不過(guò)來(lái)。 因此,最好將映射器放在方法作用域內(nèi)。就像下面的例子一樣:
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class
); // 你的應(yīng)用邏輯代碼 }
更多建議: