MyBatis 3 入門

2022-04-07 14:08 更新

安裝

要使用 MyBatis,只需將 mybatis-x.x.x.jar 文件置于類路徑(?classpath?)中即可。

如果使用Maven來構(gòu)建項目,則需將下面的依賴代碼置于 ?pom.xml? 文件中:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

從 XML 中構(gòu)建 SqlSessionFactory

每個基于 MyBatis 的應(yīng)用都是以一個 ?SqlSessionFactory的實例為核心的。?SqlSessionFactory的實例可以通過SqlSessionFactoryBuilder獲得。而 ?SqlSessionFactoryBuilder則可以從 XML 配置文件或一個預(yù)先配置的Configuration實例來構(gòu)建出SqlSessionFactory實例。

XML文件中構(gòu)建SqlSessionFactory的實例非常簡單,建議使用類路徑下的資源文件進行配置。 但也可以使用任意的輸入流(?InputStream?)實例,比如用文件路徑字符串或 ?file:// URL? 構(gòu)造的輸入流。MyBatis包含一個名叫Resources的工具類,它包含一些實用方法,使得從類路徑或其它位置加載資源文件更加容易。

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

XML 配置文件中包含了對 MyBatis 系統(tǒng)的核心設(shè)置,包括獲取數(shù)據(jù)庫連接實例的數(shù)據(jù)源(?DataSource?)以及決定事務(wù)作用域和控制方式的事務(wù)管理器(?TransactionManager?)。后面會再探討 XML 配置文件的詳細(xì)內(nèi)容,這里先給出一個簡單的示例:

<?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 文件中配置的選項,上面的示例僅羅列了最關(guān)鍵的部分。 注意 XML 頭部的聲明,它用來驗證 XML 文檔的正確性。?environment元素體中包含了事務(wù)管理和連接池的配置。?mappers元素則包含了一組映射器(?mapper?),這些映射器的 XML 映射文件包含了 SQL 代碼和映射定義信息。

不使用 XML 構(gòu)建 SqlSessionFactory

如果你更愿意直接從 Java 代碼而不是 XML 文件中創(chuàng)建配置,或者想要創(chuàng)建你自己的配置建造器,MyBatis 也提供了完整的配置類,提供了所有與 XML 文件等價的配置項。

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添加了一個映射器類(?mapper class?)。映射器類是 Java 類,它們包含 SQL 映射注解從而避免依賴 XML 文件。不過,由于 Java 注解的一些限制以及某些 MyBatis 映射的復(fù)雜性,要使用大多數(shù)高級映射(比如:嵌套聯(lián)合映射),仍然需要使用 XML 配置。有鑒于此,如果存在一個同名 XML 配置文件,MyBatis 會自動查找并加載它(在這個例子中,基于類路徑和 ?BlogMapper.class? 的類名,會加載 ?BlogMapper.xml?)。具體細(xì)節(jié)稍后討論。

從 SqlSessionFactory 中獲取 SqlSession

既然有了 ?SqlSessionFactory?,顧名思義,我們可以從中獲得 ?SqlSession的實例。?SqlSession提供了在數(shù)據(jù)庫執(zhí)行 SQL 命令所需的所有方法。你可以通過 ?SqlSession實例來直接執(zhí)行已映射的 SQL 語句。例如:

try (SqlSession session = sqlSessionFactory.openSession()) {
  Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}

誠然,這種方式能夠正常工作,對使用舊版本 MyBatis 的用戶來說也比較熟悉。但現(xiàn)在有了一種更簡潔的方式——使用和指定語句的參數(shù)和返回值相匹配的接口(比如 ?BlogMapper.class?),現(xiàn)在你的代碼不僅更清晰,更加類型安全,還不用擔(dān)心可能出錯的字符串字面值以及強制類型轉(zhuǎn)換。

例如:

try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
}

現(xiàn)在我們來探究一下這段代碼究竟做了些什么。

探究已映射的 SQL 語句

現(xiàn)在你可能很想知道 ?SqlSession和 ?Mapper到底具體執(zhí)行了些什么操作,但 SQL 語句映射是個相當(dāng)廣泛的話題,可能會占去文檔的大部分篇幅。 但為了讓你能夠了解個大概,這里會給出幾個例子。

在上面提到的例子中,一個語句既可以通過 XML 定義,也可以通過注解定義。我們先看看 XML 定義語句的方式,事實上 MyBatis 提供的所有特性都可以利用基于 XML 的映射語言來實現(xiàn),這使得 MyBatis 在過去的數(shù)年間得以流行。如果你用過舊版本的 MyBatis,你應(yīng)該對這個概念比較熟悉。 但相比于之前的版本,新版本改進了許多 XML 的配置,后面我們會提到這些改進。這里給出一個基于 XML 映射語句的示例,它應(yīng)該可以滿足上個示例中 ?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>

為了這個簡單的例子,我們似乎寫了不少配置,但其實并不多。在一個 XML 映射文件中,可以定義無數(shù)個映射語句,這樣一來,XML 頭部和文檔類型聲明部分就顯得微不足道了。文檔的其它部分很直白,容易理解。 它在命名空間 ?org.mybatis.example.BlogMapper? 中定義了一個名為 selectBlog?的映射語句,這樣你就可以用全限定名?org.mybatis.example.BlogMapper.selectBlog?來調(diào)用映射語句了,就像上面例子中那樣:

Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);

你可能會注意到,這種方式和用全限定名調(diào)用 Java 對象的方法類似。這樣,該命名就可以直接映射到在命名空間中同名的映射器類,并將已映射的 select 語句匹配到對應(yīng)名稱、參數(shù)和返回類型的方法。因此你就可以像上面那樣,不費吹灰之力地在對應(yīng)的映射器接口調(diào)用方法,就像下面這樣:

BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

第二種方法有很多優(yōu)勢,首先它不依賴于字符串字面值,會更安全一點;其次,如果你的 IDE 有代碼補全功能,那么代碼補全可以幫你快速選擇到映射好的 SQL 語句。

對命名空間的一點補充

在之前版本的 MyBatis 中,命名空間(?Namespaces?)的作用并不大,是可選的。 但現(xiàn)在,隨著命名空間越發(fā)重要,你必須指定命名空間。

命名空間的作用有兩個,一個是利用更長的全限定名來將不同的語句隔離開來,同時也實現(xiàn)了你上面見到的接口綁定。就算你覺得暫時用不到接口綁定,你也應(yīng)該遵循這里的規(guī)定,以防哪天你改變了主意。 長遠(yuǎn)來看,只要將命名空間置于合適的 Java 包命名空間之中,你的代碼會變得更加整潔,也有利于你更方便地使用 MyBatis。

命名解析:為了減少輸入量,MyBatis 對所有具有名稱的配置元素(包括語句,結(jié)果映射,緩存等)使用了如下的命名解析規(guī)則。

  • 全限定名(比如 com.mypackage.MyMapper.selectAllThings?)將被直接用于查找及使用。
  • 短名稱(比如 ?selectAllThings?)如果全局唯一也可以作為一個單獨的引用。 如果不唯一,有兩個或兩個以上的相同名稱(比如 ?com.foo.selectAllThings? 和 ?com.bar.selectAllThings?),那么使用時就會產(chǎn)生“短名稱不唯一”的錯誤,這種情況下就必須使用全限定名。

對于像 ?BlogMapper這樣的映射器類來說,還有另一種方法來完成語句映射。 它們映射的語句可以不用 XML 來配置,而可以使用 Java 注解來配置。比如,上面的 XML 示例可以被替換成如下的配置:

package org.mybatis.example;
public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

使用注解來映射簡單語句會使代碼顯得更加簡潔,但對于稍微復(fù)雜一點的語句,Java 注解不僅力不從心,還會讓你本就復(fù)雜的 SQL 語句更加混亂不堪。 因此,如果你需要做一些很復(fù)雜的操作,最好用 XML 來映射語句。

選擇何種方式來配置映射,以及認(rèn)為是否應(yīng)該要統(tǒng)一映射語句定義的形式,完全取決于你和你的團隊。 換句話說,永遠(yuǎn)不要拘泥于一種方式,你可以很輕松的在基于注解和 XML 的語句映射方式間自由移植和切換。

作用域(Scope)和生命周期

理解我們之前討論過的不同作用域和生命周期類別是至關(guān)重要的,因為錯誤的使用會導(dǎo)致非常嚴(yán)重的并發(fā)問題。

對象生命周期和依賴注入框架

依賴注入框架可以創(chuàng)建線程安全的、基于事務(wù)的SqlSession和映射器,并將它們直接注入到你的bean中,因此可以直接忽略它們的生命周期。 如果對如何通過依賴注入框架使用 MyBatis 感興趣,可以研究一下 MyBatis-Spring 或 MyBatis-Guice 兩個子項目。

SqlSessionFactoryBuilder

這個類可以被實例化、使用和丟棄,一旦創(chuàng)建了 ?SqlSessionFactory?,就不再需要它了。 因此 ?SqlSessionFactoryBuilder實例的最佳作用域是方法作用域(也就是局部方法變量)。 你可以重用 ?SqlSessionFactoryBuilder來創(chuàng)建多個 ?SqlSessionFactory實例,但最好還是不要一直保留著它,以保證所有的 XML 解析資源可以被釋放給更重要的事情。

SqlSessionFactory

?SqlSessionFactory ?一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運行期間一直存在,沒有任何理由丟棄它或重新創(chuàng)建另一個實例。 使用 ?SqlSessionFactory ?的最佳實踐是在應(yīng)用運行期間不要重復(fù)創(chuàng)建多次,多次重建 ?SqlSessionFactory ?被視為一種代碼“壞習(xí)慣”。因此 ?SqlSessionFactory的最佳作用域是應(yīng)用作用域。 有很多方法可以做到,最簡單的就是使用單例模式或者靜態(tài)單例模式。

SqlSession

每個線程都應(yīng)該有它自己的 ?SqlSession實例。?SqlSession的實例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。 絕對不能將 ?SqlSession實例的引用放在一個類的靜態(tài)域,甚至一個類的實例變量也不行。 也絕不能將SqlSession實例的引用放在任何類型的托管作用域中,比如Servlet框架中的 ?HttpSession。 如果你現(xiàn)在正在使用一種 Web 框架,考慮將SqlSession放在一個和 HTTP 請求相似的作用域中。 換句話說,每次收到 HTTP 請求,就可以打開一個 ?SqlSession?,返回一個響應(yīng)后,就關(guān)閉它。 這個關(guān)閉操作很重要,為了確保每次都能執(zhí)行關(guān)閉操作,你應(yīng)該把這個關(guān)閉操作放到 finally 塊中。 下面的示例就是一個確保 ?SqlSession關(guān)閉的標(biāo)準(zhǔn)模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的應(yīng)用邏輯代碼
}

在所有代碼中都遵循這種使用模式,可以保證所有數(shù)據(jù)庫資源都能被正確地關(guān)閉。

映射器實例

映射器是一些綁定映射語句的接口。映射器接口的實例是從SqlSession中獲得的。雖然從技術(shù)層面上來講,任何映射器實例的最大作用域與請求它們的SqlSession相同。但方法作用域才是映射器實例的最合適的作用域。 也就是說,映射器實例應(yīng)該在調(diào)用它們的方法中被獲取,使用完畢之后即可丟棄。 映射器實例并不需要被顯式地關(guān)閉。盡管在整個請求作用域保留映射器實例不會有什么問題,但是你很快會發(fā)現(xiàn),在這個作用域上管理太多像 ?SqlSession的資源會讓你忙不過來。 因此,最好將映射器放在方法作用域內(nèi)。就像下面的例子一樣:

try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // 你的應(yīng)用邏輯代碼
}


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號