【Java開源框架】持久層框架mybatis之Java API最大簡化代碼量5

既然你已經知道如何配置 MyBatis 和創建映射文件,你就已經準備好來提升技能了。MyBatis 的 Java API 就是你收穫你所做的努力的地方。正如你即將看到的,和 JDBC 相比,MyBatis 很大程度簡化了你的代碼並保持代碼簡潔,容易理解並維護。MyBatis 3 已經引入了很多重要的改進來使得 SQL 映射更加優秀。

應用目錄結構

在我們深入 Java API 之前,理解關於目錄結構的最佳實踐是很重要的。MyBatis 非常靈活,你可以用你自己的文件來做幾乎所有的事情。但是對於任一框架,都有一些最佳的方式。

讓我們看一下典型的應用目錄結構:  

/my_application
  /bin
  /devlib
  /lib                <-- MyBatis *.jar 文件在這裏。
  /src
    /org/myapp/
      /action
      /data           <-- MyBatis 配置文件在這裏, 包括映射器類, XML 配置, XML 映射文件。
        /mybatis-config.xml
        /BlogMapper.java
        /BlogMapper.xml
      /model
      /service
      /view
    /properties       <-- 在你 XML 中配置的屬性文件在這裏。
  /test
    /org/myapp/
      /action
      /data
      /model
      /service
      /view
    /properties
  /web
    /WEB-INF
      /web.xml

當然這是推薦的目錄結構,並非強制要求,但是使用一個通用的目錄結構將更利於大家溝通。

這部分內容剩餘的示例將假設你使用了這種目錄結構。

SqlSessions

使用 MyBatis 的主要 Java 接口就是 SqlSession。你可以通過這個接口來執行命令,獲取映射器和管理事務。我們會概括討論一下 SqlSession 本身,但是首先我們還是要了解如何獲取一個 SqlSession 實例。SqlSessions 是由 SqlSessionFactory 實例創建的。SqlSessionFactory 對象包含創建 SqlSession 實例的所有方法。而 SqlSessionFactory 本身是由 SqlSessionFactoryBuilder 創建的,它可以從 XML、註解或手動配置 Java 代碼來創建 SqlSessionFactory。

注意 當 Mybatis 與一些依賴注入框架(如 Spring 或者 Guice)同時使用時,SqlSessions 將被依賴注入框架所創建,所以你不需要使用 SqlSessionFactoryBuilder 或者 SqlSessionFactory,可以直接看 SqlSession 這一節。請參考 Mybatis-Spring 或者 Mybatis-Guice 手冊瞭解更多信息。

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder 有五個 build() 方法,每一種都允許你從不同的資源中創建一個 SqlSession 實例。

SqlSessionFactory build(InputStream inputStream)
SqlSessionFactory build(InputStream inputStream, String environment)
SqlSessionFactory build(InputStream inputStream, Properties properties)
SqlSessionFactory build(InputStream inputStream, String env, Properties props)
SqlSessionFactory build(Configuration config)

 

第一種方法是最常用的,它使用了一個參照了 XML 文檔或上面討論過的更特定的 mybatis-config.xml 文件的 Reader 實例。可選的參數是 environment 和 properties。environment 決定加載哪種環境,包括數據源和事務管理器。比如:

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
        ...
    <dataSource type="POOLED">
        ...
  </environment>
  <environment id="production">
    <transactionManager type="MANAGED">
        ...
    <dataSource type="JNDI">
        ...
  </environment>
</environments>

 

如果你調用了參數有 environment 的 build 方法,那麼 MyBatis 將會使用 configuration 對象來配置這個 environment。當然,如果你指定了一個不合法的 environment,你就會得到錯誤提示。如果你調用了不帶 environment 參數的 build 方法,那麼就使用默認的 environment(在上面的示例中指定爲 default="development" 的代碼)。

如果你調用了參數有 properties 實例的方法,那麼 MyBatis 就會加載那些 properties(屬性配置文件),並在配置中可用。那些屬性可以用${propName} 語法形式多次用在配置文件中。

回想一下,屬性可以從 mybatis-config.xml 中被引用,或者直接指定它。因此理解優先級是很重要的。我們在文檔前面已經提及它了,但是這裏要再次重申:


如果一個屬性存在於這些位置,那麼 MyBatis 將會按照下面的順序來加載它們:

  • 首先讀取在 properties 元素體中指定的屬性;
  • 其次,讀取從 properties 元素的類路徑 resource 或 url 指定的屬性,且會覆蓋已經指定了的重複屬性;
  • 最後,讀取作爲方法參數傳遞的屬性,且會覆蓋已經從 properties 元素體和 resource 或 url 屬性中加載了的重複屬性。

因此,通過方法參數傳遞的屬性的優先級最高,resource 或 url 指定的屬性優先級中等,在 properties 元素體中指定的屬性優先級最低。


總結一下,前四個方法很大程度上是相同的,但是由於覆蓋機制,便允許你可選地指定 environment 和/或 properties。以下給出一個從 mybatis-config.xml 文件創建 SqlSessionFactory 的示例:

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

注意到這裏我們使用了 Resources 工具類,這個類在 org.apache.ibatis.io 包中。Resources 類正如其名,會幫助你從類路徑下、文件系統或一個 web URL 中加載資源文件。看一下這個類的源代碼或者通過你的 IDE 來查看,就會看到一整套相當實用的方法。這裏給出一個簡表:

URL getResourceURL(String resource)
URL getResourceURL(ClassLoader loader, String resource)
InputStream getResourceAsStream(String resource)
InputStream getResourceAsStream(ClassLoader loader, String resource)
Properties getResourceAsProperties(String resource)
Properties getResourceAsProperties(ClassLoader loader, String resource)
Reader getResourceAsReader(String resource)
Reader getResourceAsReader(ClassLoader loader, String resource)
File getResourceAsFile(String resource)
File getResourceAsFile(ClassLoader loader, String resource)
InputStream getUrlAsStream(String urlString)
Reader getUrlAsReader(String urlString)
Properties getUrlAsProperties(String urlString)
Class classForName(String className)

最後一個 build 方法的參數爲 Configuration 實例。configuration 類包含你可能需要了解 SqlSessionFactory 實例的所有內容。Configuration 類對於配置的自查很有用,它包含查找和操作 SQL 映射(當應用接收請求時便不推薦使用)。作爲一個 Java API 的 configuration 類具有所有配置的開關,這些你已經瞭解了。這裏有一個簡單的示例,教你如何手動配置 configuration 實例,然後將它傳遞給 build() 方法來創建 SqlSessionFactory。

DataSource dataSource = BaseDataTest.createBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();

Environment environment = new Environment("development", transactionFactory, dataSource);

Configuration configuration = new Configuration(environment);
configuration.setLazyLoadingEnabled(true);
configuration.setEnhancementEnabled(true);
configuration.getTypeAliasRegistry().registerAlias(Blog.class);
configuration.getTypeAliasRegistry().registerAlias(Post.class);
configuration.getTypeAliasRegistry().registerAlias(Author.class);
configuration.addMapper(BoundBlogMapper.class);
configuration.addMapper(BoundAuthorMapper.class);

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configuration);

現在你就獲得一個可以用來創建 SqlSession 實例的 SqlSessionFactory 了!

SqlSessionFactory

SqlSessionFactory 有六個方法創建 SqlSession 實例。通常來說,當你選擇這些方法時你需要考慮以下幾點:

  • 事務處理:我需要在 session 使用事務或者使用自動提交功能(auto-commit)嗎?(通常意味着很多數據庫和/或 JDBC 驅動沒有事務)
  • 連接:我需要依賴 MyBatis 獲得來自數據源的配置嗎?還是使用自己提供的配置?
  • 執行語句:我需要 MyBatis 複用預處理語句和/或批量更新語句(包括插入和刪除)嗎?

基於以上需求,有下列已重載的多個 openSession() 方法供使用。

SqlSession openSession()
SqlSession openSession(boolean autoCommit)
SqlSession openSession(Connection connection)
SqlSession openSession(TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)
Configuration getConfiguration();

默認的 openSession()方法沒有參數,它會創建有如下特性的 SqlSession: 

  • 會開啓一個事務(也就是不自動提交)。
  • 將從由當前環境配置的 DataSource 實例中獲取 Connection 對象。
  • 事務隔離級別將會使用驅動或數據源的默認設置。
  • 預處理語句不會被複用,也不會批量處理更新。

這些方法大都是可讀性強的。向 autoCommit 可選參數傳遞 true 值即可開啓自動提交功能。若要使用自己的 Connection 實例,傳遞一個 Connection 實例給 connection 參數即可。注意並未覆寫同時設置 Connection 和 autoCommit 兩者的方法,因爲 MyBatis 會使用正在使用中的、設置了 Connection 的環境。MyBatis 爲事務隔離級別調用使用了一個 Java 枚舉包裝器,稱爲 TransactionIsolationLevel,若不使用它,將使用 JDBC 所支持五個隔離級(NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE),並按它們預期的方式來工作。

 

還有一個可能對你來說是新見到的參數,就是 ExecutorType。這個枚舉類型定義了三個值:

  • ExecutorType.SIMPLE:這個執行器類型不做特殊的事情。它爲每個語句的執行創建一個新的預處理語句。
  • ExecutorType.REUSE:這個執行器類型會複用預處理語句。
  • ExecutorType.BATCH:這個執行器會批量執行所有更新語句,如果 SELECT 在它們中間執行,必要時請把它們區分開來以保證行爲的易讀性。

注意 在 SqlSessionFactory 中還有一個方法我們沒有提及,就是 getConfiguration()。這 個方法會返回一個 Configuration 實例,在運行時你可以使用它來自檢 MyBatis 的配置。

注意 如果你使用的是 MyBatis 之前的版本,你要重新調用 openSession,因爲舊版本的 session、事務和批量操作是分離開來的。如果使用的是新版本,那麼就不必這麼做了,因爲它們現在都包含在 session 的作用域內了。你不必再單獨處理事務或批量操作就能得到想要的全部效果。

SqlSession

正如上面所提到的,SqlSession 實例在 MyBatis 中是非常強大的一個類。在這裏你會看到所有執行語句、提交或回滾事務和獲取映射器實例的方法。

在 SqlSession 類中有超過 20 個方法,所以將它們組合成易於理解的分組。

執行語句方法

這些方法被用來執行定義在 SQL 映射的 XML 文件中的 SELECT、INSERT、UPDATE 和 DELETE 語句。它們都會自行解釋,每一句都使用語句的 ID 屬性和參數對象,參數可以是原生類型(自動裝箱或包裝類)、JavaBean、POJO 或 Map。

<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<T> Cursor<T> selectCursor(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)

selectOne 和 selectList 的不同僅僅是 selectOne 必須返回一個對象或 null 值。如果返回值多於一個,那麼就會拋出異常。如果你不知道返回對象的數量,請使用 selectList。如果需要查看返回對象是否存在,可行的方案是返回一個值即可(0 或 1)。selectMap 稍微特殊一點,因爲它會將返回的對象的其中一個屬性作爲 key 值,將對象作爲 value 值,從而將多結果集轉爲 Map 類型值。因爲並不是所有語句都需要參數,所以這些方法都重載成不需要參數的形式。  

A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.

try (Cursor<MyEntity> entities = session.selectCursor(statement, param)) {
   for (MyEntity entity:entities) {
      // process one entity
   }
}

The value returned by the insert, update and delete methods indicate the number of rows affected by the statement.

<T> T selectOne(String statement)
<E> List<E> selectList(String statement)
<T> Cursor<T> selectCursor(String statement)
<K,V> Map<K,V> selectMap(String statement, String mapKey)
int insert(String statement)
int update(String statement)
int delete(String statement)

 

最後,還有 select 方法的三個高級版本,它們允許你限制返回行數的範圍,或者提供自定義結果控制邏輯,這通常在數據集合龐大的情形下使用。

<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
void select (String statement, Object parameter, ResultHandler<T> handler)
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)

 

RowBounds 參數會告訴 MyBatis 略過指定數量的記錄,還有限制返回結果的數量。RowBounds 類有一個構造方法來接收 offset 和 limit,另外,它們是不可二次賦值的。

int offset = 100;
int limit = 25;
RowBounds rowBounds = new RowBounds(offset, limit);

所以在這方面,不同的驅動能夠取得不同級別的高效率。爲了取得最佳的表現,請使用結果集的 SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 的類型(換句話說:不用 FORWARD_ONLY)。

ResultHandler 參數允許你按你喜歡的方式處理每一行。你可以將它添加到 List 中、創建 Map 和 Set,或者丟棄每個返回值都可以,它取代了僅保留執行語句過後的總結果列表的死板結果。你可以使用 ResultHandler 做很多事,並且這是 MyBatis 自身內部會使用的方法,以創建結果集列表。

Since 3.4.6, ResultHandler passed to a CALLABLE statement is used on every REFCURSOR output parameter of the stored procedure if there is any.

它的接口很簡單。

package org.apache.ibatis.session;
public interface ResultHandler<T> {
  void handleResult(ResultContext<? extends T> context);
}

ResultContext 參數允許你訪問結果對象本身、被創建的對象數目、以及返回值爲 Boolean 的 stop 方法,你可以使用此 stop 方法來停止 MyBatis 加載更多的結果。

 

使用 ResultHandler 的時候需要注意以下兩種限制:

  • 從被 ResultHandler 調用的方法返回的數據不會被緩存。
  • 當使用結果映射集(resultMap)時,MyBatis 大多數情況下需要數行結果來構造外鍵對象。如果你正在使用 ResultHandler,你可以給出外鍵(association)或者集合(collection)尚未賦值的對象。

批量立即更新方法

有一個方法可以刷新(執行)存儲在 JDBC 驅動類中的批量更新語句。當你將 ExecutorType.BATCH 作爲 ExecutorType 使用時可以採用此方法。

List<BatchResult> flushStatements()

事務控制方法

控制事務作用域有四個方法。當然,如果你已經設置了自動提交或你正在使用外部事務管理器,這就沒有任何效果了。然而,如果你正在使用 JDBC 事務管理器,由Connection 實例來控制,那麼這四個方法就會派上用場:  

void commit()
void commit(boolean force)
void rollback()
void rollback(boolean force)

默認情況下 MyBatis 不會自動提交事務,除非它偵測到有插入、更新或刪除操作改變了數據庫。如果你已經做出了一些改變而沒有使用這些方法,那麼你可以傳遞 true 值到 commit 和 rollback 方法來保證事務被正常處理(注意,在自動提交模式或者使用了外部事務管理器的情況下設置 force 值對 session 無效)。很多時候你不用調用 rollback(),因爲 MyBatis 會在你沒有調用 commit 時替你完成回滾操作。然而,如果你需要在支持多提交和回滾的 session 中獲得更多細粒度控制,你可以使用回滾操作來達到目的。

注意 MyBatis-Spring 和 MyBatis-Guice 提供了聲明事務處理,所以如果你在使用 Mybatis 的同時使用了Spring 或者 Guice,那麼請參考它們的手冊以獲取更多的內容。

本地緩存

Mybatis 使用到了兩種緩存:本地緩存(local cache)和二級緩存(second level cache)。

每當一個新 session 被創建,MyBatis 就會創建一個與之相關聯的本地緩存。任何在 session 執行過的查詢語句本身都會被保存在本地緩存中,那麼,相同的查詢語句和相同的參數所產生的更改就不會二度影響數據庫了。本地緩存會被增刪改、提交事務、關閉事務以及關閉 session 所清空。

默認情況下,本地緩存數據可在整個 session 的週期內使用,這一緩存需要被用來解決循環引用錯誤和加快重複嵌套查詢的速度,所以它可以不被禁用掉,但是你可以設置 localCacheScope=STATEMENT 表示緩存僅在語句執行時有效。

注意,如果 localCacheScope 被設置爲 SESSION,那麼 MyBatis 所返回的引用將傳遞給保存在本地緩存裏的相同對象。對返回的對象(例如 list)做出任何更新將會影響本地緩存的內容,進而影響存活在 session 生命週期中的緩存所返回的值。因此,不要對 MyBatis 所返回的對象作出更改,以防後患。

你可以隨時調用以下方法來清空本地緩存:

void clearCache()

確保 SqlSession 被關閉

void close()

你必須保證的最重要的事情是你要關閉所打開的任何 session。保證做到這點的最佳方式是下面的工作模式:

SqlSession session = sqlSessionFactory.openSession();
try {
    // following 3 lines pseudocod for "doing some work"
    session.insert(...);
    session.update(...);
    session.delete(...);
    session.commit();
} finally {
    session.close();
}

還有,如果你正在使用jdk 1.7以上的版本還有MyBatis 3.2以上的版本,你可以使用try-with-resources語句:

try (SqlSession session = sqlSessionFactory.openSession()) {
    // following 3 lines pseudocode for "doing some work"
    session.insert(...);
    session.update(...);
    session.delete(...);
    session.commit();
}

 

注意 就像 SqlSessionFactory,你可以通過調用當前使用中的 SqlSession 的 getConfiguration 方法來獲得 Configuration 實例。

Configuration getConfiguration()

使用映射器

<T> T getMapper(Class<T> type)

上述的各個 insert、update、delete 和 select 方法都很強大,但也有些繁瑣,可能會產生類型安全問題並且對於你的 IDE 和單元測試也沒有實質性的幫助。在上面的入門章節中我們已經看到了一個使用映射器的示例。

因此,一個更通用的方式來執行映射語句是使用映射器類。一個映射器類就是一個僅需聲明與 SqlSession 方法相匹配的方法的接口類。下面的示例展示了一些方法簽名以及它們是如何映射到 SqlSession 上的。

public interface AuthorMapper {
  // (Author) selectOne("selectAuthor",5);
  Author selectAuthor(int id);
  // (List<Author>) selectList(“selectAuthors”)
  List<Author> selectAuthors();
  // (Map<Integer,Author>) selectMap("selectAuthors", "id")
  @MapKey("id")
  Map<Integer, Author> selectAuthors();
  // insert("insertAuthor", author)
  int insertAuthor(Author author);
  // updateAuthor("updateAuthor", author)
  int updateAuthor(Author author);
  // delete("deleteAuthor",5)
  int deleteAuthor(int id);
}

總之,每個映射器方法簽名應該匹配相關聯的 SqlSession 方法,而字符串參數 ID 無需匹配。相反,方法名必須匹配映射語句的 ID。

此外,返回類型必須匹配期望的結果類型,單返回值時爲所指定類的值,多返回值時爲數組或集合。所有常用的類型都是支持的,包括:原生類 型、Map、POJO 和 JavaBean。

 

注意 映射器接口不需要去實現任何接口或繼承自任何類。只要方法可以被唯一標識對應的映射語句就可以了。

 

注意 映射器接口可以繼承自其他接口。當使用 XML 來構建映射器接口時要保證語句被包含在合適的命名空間中。而且,唯一的限制就是你不能在兩個繼承關係的接口中擁有相同的方法簽名(潛在的危險做法不可取)。

 

你可以傳遞多個參數給一個映射器方法。如果你這樣做了,默認情況下它們將會以 "param" 字符串緊跟着它們在參數列表中的位置來命名,比如:#{param1}、#{param2}等。如果你想改變參數的名稱(只在多參數情況下),那麼你可以在參數上使用 @Param("paramName") 註解。

你也可以給方法傳遞一個 RowBounds 實例來限制查詢結果。

映射器註解

因爲最初設計時,MyBatis 是一個 XML 驅動的框架。配置信息是基於 XML 的,而且映射語句也是定義在 XML 中的。而到了 MyBatis 3,就有新選擇了。MyBatis 3 構建在全面且強大的基於 Java 語言的配置 API 之上。這個配置 API 是基於 XML 的 MyBatis 配置的基礎,也是新的基於註解配置的基礎。註解提供了一種簡單的方式來實現簡單映射語句,而不會引入大量的開銷。

 

注意 不幸的是,Java 註解的的表達力和靈活性十分有限。儘管很多時間都花在調查、設計和試驗上,最強大的 MyBatis 映射並不能用註解來構建——並不是在開玩笑,的確是這樣。比方說,C#屬性就沒有這些限制,因此 MyBatis.NET 將會比 XML 有更豐富的選擇。也就是說,基於 Java 註解的配置離不開它的特性。

註解如下表所示:

註解 使用對象 相對應的 XML 描述
@CacheNamespace <cache> 爲給定的命名空間(比如類)配置緩存。屬性有:implemetation, eviction, flushInterval, size, readWrite, blocking 和properties。
@Property N/A <property> 指定參數值或佔位值(placeholder)(能被 mybatis-config.xml內的配置屬性覆蓋)。屬性有:name, value。(僅在MyBatis 3.4.2以上版本生效)
@CacheNamespaceRef <cacheRef> 參照另外一個命名空間的緩存來使用。屬性有:value, name。如果你使用了這個註解,你應設置 value 或者 name 屬性的其中一個。value 屬性用於指定 Java 類型而指定命名空間(命名空間名就是指定的 Java 類型的全限定名),name 屬性(這個屬性僅在MyBatis 3.4.2以上版本生效)直接指定了命名空間的名字。
@ConstructorArgs 方法 <constructor> 收集一組結果傳遞給一個結果對象的構造方法。屬性有:value,它是形式參數數組。
@Arg N/A
  • <arg>
  • <idArg>
單參數構造方法,是 ConstructorArgs 集合的一部分。屬性有:id, column, javaType, jdbcType, typeHandler, select 和 resultMap。id 屬性是布爾值,來標識用於比較的屬性,和<idArg> XML 元素相似。
@TypeDiscriminator 方法 <discriminator> 一組實例值被用來決定結果映射的表現。屬性有:column, javaType, jdbcType, typeHandler 和 cases。cases 屬性是實例數組。
@Case N/A <case> 單獨實例的值和它對應的映射。屬性有:value, type, results。results 屬性是結果數組,因此這個註解和實際的 ResultMap 很相似,由下面的 Results 註解指定。
@Results 方法 <resultMap> 結果映射的列表,包含了一個特別結果列如何被映射到屬性或字段的詳情。屬性有:value, id。value 屬性是 Result 註解的數組。這個 id 的屬性是結果映射的名稱。
@Result N/A
  • <result>
  • <id>
在列和屬性或字段之間的單獨結果映射。屬性有:id, column, javaType, jdbcType, typeHandler, one, many。id 屬性是一個布爾值,來標識應該被用於比較(和在 XML 映射中的<id>相似)的屬性。one 屬性是單獨的聯繫,和 <association> 相似,而 many 屬性是對集合而言的,和<collection>相似。它們這樣命名是爲了避免名稱衝突。
@One N/A <association> 複雜類型的單獨屬性值映射。屬性有:select,已映射語句(也就是映射器方法)的全限定名,它可以加載合適類型的實例。fetchType會覆蓋全局的配置參數 lazyLoadingEnabled。注意聯合映射在註解 API中是不支持的。這是因爲 Java 註解的限制,不允許循環引用。
@Many N/A <collection> 映射到複雜類型的集合屬性。屬性有:select,已映射語句(也就是映射器方法)的全限定名,它可以加載合適類型的實例的集合,fetchType 會覆蓋全局的配置參數 lazyLoadingEnabled。注意 聯合映射在註解 API中是不支持的。這是因爲 Java 註解的限制,不允許循環引用
@MapKey 方法   這是一個用在返回值爲 Map 的方法上的註解。它能夠將存放對象的 List 轉化爲 key 值爲對象的某一屬性的 Map。屬性有: value,填入的是對象的屬性名,作爲 Map 的 key 值。
@Options 方法 映射語句的屬性 這個註解提供訪問大範圍的交換和配置選項的入口,它們通常在映射語句上作爲屬性出現。Options 註解提供了通俗易懂的方式來訪問它們,而不是讓每條語句註解變複雜。屬性有:useCache=true, flushCache=FlushCachePolicy.DEFAULT, resultSetType=DEFAULT, statementType=PREPARED, fetchSize=-1, timeout=-1, useGeneratedKeys=false, keyProperty="", keyColumn="", resultSets=""。值得一提的是, Java 註解無法指定 null 值。因此,一旦你使用了 Options 註解,你的語句就會被上述屬性的默認值所影響。要注意避免默認值帶來的預期以外的行爲。

       注意: keyColumn 屬性只在某些數據庫中有效(如 Oracle、PostgreSQL等)。請在插入語句一節查看更多關於 keyColumn 和 keyProperty 兩者的有效值詳情。
  • @Insert
  • @Update
  • @Delete
  • @Select
方法
  • <insert>
  • <update>
  • <delete>
  • <select>
這四個註解分別代表將會被執行的 SQL 語句。它們用字符串數組(或單個字符串)作爲參數。如果傳遞的是字符串數組,字符串之間先會被填充一個空格再連接成單個完整的字符串。這有效避免了以 Java 代碼構建 SQL 語句時的“丟失空格”的問題。然而,你也可以提前手動連接好字符串。屬性有:value,填入的值是用來組成單個 SQL 語句的字符串數組。
  • @InsertProvider
  • @UpdateProvider
  • @DeleteProvider
  • @SelectProvider
方法
  • <insert>
  • <update>
  • <delete>
  • <select>
允許構建動態 SQL。這些備選的 SQL 註解允許你指定類名和返回在運行時執行的 SQL 語句的方法。(自從MyBatis 3.4.6開始,你可以用 CharSequence 代替 String 來返回類型返回值了。)當執行映射語句的時候,MyBatis 會實例化類並執行方法,類和方法就是填入了註解的值。你可以把已經傳遞給映射方法了的對象作爲參數,"Mapper interface type" 和 "Mapper method" and "Database ID" 會經過 ProviderContext (僅在MyBatis 3.4.5及以上支持)作爲參數值。(MyBatis 3.4及以上的版本,支持多參數傳入) 屬性有: type, method。 type 屬性需填入類。 method 需填入該類定義了的方法名 (Since 3.5.1, you can omit method attribute, the MyBatis will resolve a target method via the ProviderMethodResolver interface. If not resolve by it, the MyBatis use the reserved fallback method that named provideSql)。 注意 接下來的小節將會討論類,能幫助你更輕鬆地構建動態 SQL。
@Param 參數 N/A 如果你的映射方法的形參有多個,這個註解使用在映射方法的參數上就能爲它們取自定義名字。若不給出自定義名字,多參數(不包括 RowBounds 參數)則先以 "param" 作前綴,再加上它們的參數位置作爲參數別名。例如 #{param1}, #{param2},這個是默認值。如果註解是 @Param("person"),那麼參數就會被命名爲 #{person}。
@SelectKey 方法 <selectKey> 這個註解的功能與 <selectKey> 標籤完全一致,用在已經被 @Insert 或 @InsertProvider 或 @Update 或 @UpdateProvider 註解了的方法上。若在未被上述四個註解的方法上作 @SelectKey 註解則視爲無效。如果你指定了 @SelectKey 註解,那麼 MyBatis 就會忽略掉由 @Options 註解所設置的生成主鍵或設置(configuration)屬性。屬性有:statement 填入將會被執行的 SQL 字符串數組,keyProperty 填入將會被更新的參數對象的屬性的值,before 填入 true 或 false 以指明 SQL 語句應被在插入語句的之前還是之後執行。resultType 填入 keyProperty 的 Java 類型和用 Statement、 PreparedStatement 和 CallableStatement 中的 STATEMENT、 PREPARED 或 CALLABLE 中任一值填入 statementType。默認值是 PREPARED。
@ResultMap 方法 N/A 這個註解給 @Select 或者 @SelectProvider 提供在 XML 映射中的 <resultMap> 的id。這使得註解的 select 可以複用那些定義在 XML 中的 ResultMap。如果同一 select 註解中還存在 @Results 或者 @ConstructorArgs,那麼這兩個註解將被此註解覆蓋。
@ResultType 方法 N/A 此註解在使用了結果處理器的情況下使用。在這種情況下,返回類型爲 void,所以 Mybatis 必須有一種方式決定對象的類型,用於構造每行數據。如果有 XML 的結果映射,請使用 @ResultMap註解。如果結果類型在 XML 的 <select> 節點中指定了,就不需要其他的註解了。其他情況下則使用此註解。比如,如果 @Select 註解在一個將使用結果處理器的方法上,那麼返回類型必須是 void 並且這個註解(或者@ResultMap)必選。這個註解僅在方法返回類型是 void 的情況下生效。
@Flush 方法 N/A 如果使用了這個註解,定義在 Mapper 接口中的方法能夠調用 SqlSession#flushStatements() 方法。(Mybatis 3.3及以上)

映射申明樣例

這個例子展示瞭如何使用 @SelectKey 註解來在插入前讀取數據庫序列的值:

@Insert("insert into table3 (id, name) values(#{nameId}, #{name})")
@SelectKey(statement="call next value for TestSequence", keyProperty="nameId", before=true, resultType=int.class)
int insertTable3(Name name);

這個例子展示瞭如何使用 @SelectKey 註解來在插入後讀取數據庫識別列的值:

@Insert("insert into table2 (name) values(#{name})")
@SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
int insertTable2(Name name);

這個例子展示瞭如何使用 @Flush 註解去調用 SqlSession#flushStatements():

@Flush
List<BatchResult> flush();

這些例子展示瞭如何通過指定 @Result 的 id 屬性來命名結果集:

@Results(id = "userResult", value = {
  @Result(property = "id", column = "uid", id = true),
  @Result(property = "firstName", column = "first_name"),
  @Result(property = "lastName", column = "last_name")
})
@Select("select * from users where id = #{id}")
User getUserById(Integer id);

@Results(id = "companyResults")
@ConstructorArgs({
  @Arg(property = "id", column = "cid", id = true),
  @Arg(property = "name", column = "name")
})
@Select("select * from company where id = #{id}")
Company getCompanyById(Integer id);

這個例子展示了單一參數使用 @SqlProvider 註解:

@SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName")
List<User> getUsersByName(String name);

class UserSqlBuilder {
  public static String buildGetUsersByName(final String name) {
    return new SQL(){{
      SELECT("*");
      FROM("users");
      if (name != null) {
        WHERE("name like #{value} || '%'");
      }
      ORDER_BY("id");
    }}.toString();
  }
}

這個例子展示了多參數使用 @SqlProvider 註解:

@SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName")
List<User> getUsersByName(
    @Param("name") String name, @Param("orderByColumn") String orderByColumn);

class UserSqlBuilder {

  // If not use @Param, you should be define same arguments with mapper method
  public static String buildGetUsersByName(
      final String name, final String orderByColumn) {
    return new SQL(){{
      SELECT("*");
      FROM("users");
      WHERE("name like #{name} || '%'");
      ORDER_BY(orderByColumn);
    }}.toString();
  }

  // If use @Param, you can define only arguments to be used
  public static String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) {
    return new SQL(){{
      SELECT("*");
      FROM("users");
      WHERE("name like #{name} || '%'");
      ORDER_BY(orderByColumn);
    }}.toString();
  }
}

This example shows usage the default implementation of ProviderMethodResolver(available since MyBatis 3.5.1 or later):

@SelectProvider(type = UserSqlProvider.class)
List<User> getUsersByName(String name);

// Implements the ProviderMethodResolver on your provider class
class UserSqlProvider implements ProviderMethodResolver {
  // In default implementation, it will resolve a method that method name is matched with mapper method
  public static String getUsersByName(final String name) {
    return new SQL(){{
      SELECT("*");
      FROM("users");
      if (name != null) {
        WHERE("name like #{value} || '%'");
      }
      ORDER_BY("id");
    }}.toString();
  }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章