天天看點

MyBatis中SqlSessionFactory和SqlSession簡解

mybatis官網中文文檔:​​https://mybatis.org/mybatis-3/zh/sqlmap-xml.html​​

mybatis-spring官方中文文檔:​​https://mybatis.org/spring/zh/index.html​​

【1】SqlSessionFactoryBuilder

這個類可以被初始化、使用和丢棄,如果你已經建立好了一個SqlSessionFactory 後就不用再保留它。

是以,SqlSessionFactoryBuilder 的​

​最好作用域是方法體内​

比如說定義一個方法變量。你可以重複使用SqlSessionFactoryBuilder 生成多個SqlSessionFactory 執行個體,但是最好不要強行保留,因為XML 的解析資源要用來做其它更重要的事。

mybatis3.4.1版本中,SqlSessionFactoryBuilder中擷取SqlSessionFactory執行個體如下(每一種都允許你從不同的資源中建立一個 SqlSessionFactory 執行個體):

SqlSessionFactory build(Reader reader)
SqlSessionFactory build(Reader reader, String environment) 
SqlSessionFactory build(Reader reader, Properties properties) 
SqlSessionFactory build(Reader reader, String environment, Properties properties)
SqlSessionFactory build(InputStream inputStream)
SqlSessionFactory build(InputStream inputStream, String environment)
SqlSessionFactory build(InputStream inputStream, Properties properties)
SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)
SqlSessionFactory build(Configuration config)      

SqlSessionFactoryBuilder完整源碼

package org.apache.ibatis.session;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

/**
 * Builds {@link SqlSession} instances.
 *
 * @author Clinton Begin
 */
public class SqlSessionFactoryBuilder {

  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }

  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }
//核心方法一
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
    //這裡非常非常重要,會解析mybatis的相關配置,以及XXXMapper.xml中的結點、xxxxMapper.java中的注解
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }

  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }
//核心方法二
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
//核心方法三,根據解析到的Configuration 建立一個預設的DefaultSqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

}      

上面有兩種建立DefaultSqlSessionFactory的核心方法,提供了諸多重載方法,最後調用核心方法三得到一個預設的DefaultSqlSessionFactory執行個體。

【2】SqlSessionFactory

SqlSessionFactory一旦建立,SqlSessionFactory 就會在整個應用過程中始終存在。是以沒有理由去銷毀和再建立它,一個應用運作中也不建議多次建立SqlSessionFactory。

是以SqlSessionFactory​

​最好的作用域是Application​

​。

可以有多種方法實作,最簡單的方法是​

​單例模式或者是靜态單例模式​

​。然而這也不是廣泛贊成和好用的。反而,使用Google Guice 或Spring 來進行依賴反射會更好。這些架構允許你生成管理器來管理SqlSessionFactory 的單例生命周期。

SqlSessionFactory 接口源碼

package org.apache.ibatis.session;

import java.sql.Connection;

/**
根據一個資料庫連接配接或者資料源建立一個sqlsession執行個體,create..out of 根據/由什麼建立
 * Creates an {@link SqlSession} out of a connection or a DataSource
 * @author Clinton Begin
 */
public interface SqlSessionFactory {

  SqlSession openSession();

  SqlSession openSession(boolean autoCommit);

  SqlSession openSession(Connection connection);

  SqlSession openSession(TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType);

  SqlSession openSession(ExecutorType execType, boolean autoCommit);

  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType, Connection connection);

  Configuration getConfiguration();

}      

其預設實作類是DefaultSqlSessionFactory。

SqlSessionFactory 有六個方法建立 SqlSession 執行個體

SqlSessionFactory 有六個方法建立 SqlSession 執行個體。通常來說,當你選擇其中一個方法時,你需要考慮以下幾點:

  • 事務處理:你希望在 session 作用域中使用事務作用域,還是使用自動送出(auto-commit)?(對很多資料庫和/或 JDBC 驅動來說,等同于關閉事務支援)
  • 資料庫連接配接:你希望 MyBatis 幫你從已配置的資料源擷取連接配接,還是使用自己提供的連接配接?
  • 語句執行:你希望 MyBatis 複用 PreparedStatement 和/或批量更新語句(包括插入語句和删除語句)嗎?

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

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

預設的 openSession() 方法沒有參數,它會建立具備如下特性的 SqlSession:

  • 事務作用域将會開啟(也就是不自動送出)。
  • 将由目前環境配置的 DataSource 執行個體中擷取 Connection 對象。
  • 事務隔離級别将會使用驅動或資料源的預設設定。
  • 預處理語句不會被複用,也不會批量處理更新。

相信你已經能從方法簽名中知道這些方法的差別。向 ​

​autoCommit​

​​ 可選參數傳遞 true 值即可開啟自動送出功能。若要使用自己的 Connection 執行個體,傳遞一個 Connection 執行個體給 connection 參數即可。注意,我們沒有提供同時設定 ​

​Connection​

​​ 和 ​

​autoCommit​

​​ 的方法,這是因為 MyBatis 會依據傳入的 ​

​Connection​

​​ 來決定是否啟用 ​

​autoCommit​

​​。對于事務隔離級别,MyBatis 使用了一個 Java 枚舉包裝器來表示,稱為 ​

​TransactionIsolationLevel​

​​,事務隔離級别支援 JDBC 的五個隔離級别(​

​NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE​

​),并且與預期的行為一緻。

你可能對 ExecutorType 參數感到陌生。這個枚舉類型定義了三個值:

  • ​ExecutorType.SIMPLE​

    ​:該類型的執行器沒有特别的行為。它為每個語句的執行建立一個新的預處理語句。
  • ​ExecutorType.REUSE​

    ​:該類型的執行器會複用預處理語句。
  • ​ExecutorType.BATCH​

    ​:該類型的執行器會批量執行所有更新語句,如果 SELECT 在多個更新中間執行,将在必要時将多條更新語句分隔開來,以友善了解。
批量操作是在​

​session.commit()​

​​以後才發送sql語句給資料庫進行執行的

如果我們想讓其提前執行,以友善後續可能的查詢操作擷取資料,我們可以使用​​

​sqlSession.flushStatements()​

​方法,讓其直接沖刷到資料庫進行執行。

提示 在 ​

​SqlSessionFactory​

​​ 中還有一個方法我們沒有提及,就是 ​

​getConfiguration​

​​()。這個方法會傳回一個 ​

​Configuration​

​ 執行個體,你可以在運作時使用它來檢查 MyBatis 的配置。

提示 如果你使用過 MyBatis 的舊版本,可能還記得 session、事務和批量操作是互相獨立的。在新版本中則不是這樣。上述三者都包含在 session 作用域内。你不必分别處理事務或批量操作就能得到想要的全部效果。

預設的openSession() 方法沒有參數,它會建立有如下特性的

  • 會開啟一個事務(也就是不自動送出)
  • 連接配接對象會從由活動環境配置的資料源執行個體得到。
  • 事務隔離級别将會使用驅動或資料源的預設設定。
  • 預處理語句不會被複用,也不會批量處理更新。

【3】非線程安全的SqlSession

使用 MyBatis 的主要 Java 接口就是 SqlSession。你可以通過這個接口來執行指令,擷取映射器執行個體和管理事務。

每個線程都有自己的SqlSession 執行個體,​

​SqlSession 執行個體是不能被共享,也不是線程安全的​

​。是以最好使用​

​request 作用域​

​或者​

​方法體作用域​

​。

不要使用類的靜态變量來引用一個SqlSession 執行個體,甚至不要使用類的一個執行個體變量來引用。否則,會有線程安全問題。

永遠不要在一個被管理域中引用SqlSession

比如說在Servlet 中的HttpSession 中。如果你正在使用WEB 架構,應該讓SqlSession 跟随HTTP 請求的相似作用域。也就是說,在收到一個HTTP 請求過後,打開SqlSession,等傳回一個回應以後,立馬關掉這個SqlSession。關閉SqlSession 是非常重要的,你必須要確定SqlSession 在finally 方法體中正常關閉。

SqlSession可以直接調用​

​方法的id進行資料庫操作​

​,但是我們一般還是推薦使用SqlSession擷取到Dao接口的代理類,執行代理對象的方法,可以更安全的進行類型檢查操作。

如下代碼所示,是調用方法ID進行資料庫操作:

SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

SqlSession openSession = sqlSessionFactory.openSession();
try {
  Employee employee = openSession.selectOne(
      "com.mybatis.EmployeeMapper.selectEmp", 1);
  System.out.println(employee);
} finally {
  openSession.close();
}      

正如之前所提到的,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​

​​。如果需要檢視某個對象是否存在,最好的辦法是​

​查詢一個 count 值(0 或 1)​

​​。​

​selectMap​

​​ 稍微特殊一點,它會将傳回對象的其中一個屬性作為 ​

​key​

​​ 值,将對象作為 ​

​value​

​ 值,進而将多個結果集轉為 Map 類型值。由于并不是所有語句都需要參數,是以這些方法都具有一個不需要參數的重載形式。

遊标(Cursor)與清單(List)傳回的結果相同,不同的是,遊标借助疊代器實作了資料的惰性加載。

try (Cursor<MyEntity> entities = session.selectCursor(statement, param)) {
   for (MyEntity entity:entities) {
      // 處理單個實體
   }
}
insert、update 以及 delete 方法傳回的值表示受該語句影響的行數。

<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);      

資料庫驅動決定了略過記錄時的查詢效率。為了獲得最佳的性能,建議将 ​

​ResultSet​

​​ 類型設定為 ​

​SCROLL_SENSITIVE​

​​ 或 ​

​SCROLL_INSENSITIVE​

​​(​

​換句話說:不要使用 FORWARD_ONLY​

​)。

​ResultHandler​

​ 參數允許自定義每行結果的處理過程。你可以将它添加到 List 中、建立 Map 和 Set,甚至丢棄每個傳回值,隻保留計算後的統計結果。你可以使用 ResultHandler 做很多事,這其實就是 MyBatis 建構 結果清單的内部實作辦法。

從版本 3.4.6 開始,​

​ResultHandler​

​​ 會在存儲過程的 ​

​REFCURSOR​

​​ 輸出參數中傳遞使用的 ​

​CALLABLE​

​ 語句。

它的接口很簡單:

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​

    ​)中尚未被完整填充的對象。

立即批量更新方法

當你将 ​

​ExecutorType​

​​ 設定為 ​

​ExecutorType.BATCH​

​ 時,可以使用這個方法清除(執行)緩存在 JDBC 驅動類中的批量更新語句。

List<BatchResult> flushStatements()      

事務控制方法

有四個方法用來控制事務作用域。當然,如果你已經設定了自動送出或你使用了外部事務管理器,這些方法就沒什麼作用了。然而,如果你正在使用由 Connection 執行個體控制的 JDBC 事務管理器,那麼這四個方法就會派上用場:

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

​預設情況下 MyBatis 不會自動送出事務,除非它偵測到調用了插入、更新或删除方法改變了資料庫​

​​。如果你沒有使用這些方法送出修改,那麼你可以在 ​

​commit​

​​ 和 ​

​rollback​

​​ 方法參數中傳入 true 值,來保證事務被正常送出(注意,在自動送出模式或者使用了外部事務管理器的情況下,設定 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 (SqlSession session = sqlSessionFactory.openSession()) {
    // 假設下面三行代碼是你的業務邏輯
    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​

​。

此外,傳回類型必須比對期望的結果類型,傳回單個值時,傳回類型應該是傳回值的類,傳回多個值時,則為數組或集合類,另外也可以是​

​遊标(Cursor)​

​​。所有常用的類型都是支援的,包括:​

​原始類型、Map、POJO 和 JavaBean​

​。

提示 映射器接口不需要去實作任何接口或繼承自任何類。隻要方法簽名可以被用來唯一識别對應的映射語句就可以了。

提示 映射器接口可以繼承自其他接口。在使用 XML 來綁定映射器接口時,保證語句處于合适的命名空間中即可。唯一的限制是,不能在兩個具有繼承關系的接口中擁有相同的方法簽名(這是潛在的危險做法,不可取)。

你可以傳遞多個參數給一個映射器方法。在多個參數的情況下,預設它們将會以 param 加上它們在參數清單中的位置來命名,比如:​

​#{param1}、#{param2}​

​​等。如果你想(在有多個參數時)自定義參數的名稱,那麼你可以在參數上使用 ​

​@Param("paramName")​

​ 注解。

你也可以給方法傳遞一個 ​

​RowBounds​

​ 執行個體來限制查詢結果。

映射器注解

設計初期的 MyBatis 是一個 XML 驅動的架構。配置資訊是基于 XML 的,映射語句也是定義在 XML 中的。而在 MyBatis 3 中,我們提供了其它的配置方式。MyBatis 3 建構在全面且強大的基于 Java 語言的配置 API 之上。它是 XML 和注解配置的基礎。注解提供了一種簡單且低成本的方式來實作簡單的映射語句。

提示 不幸的是,Java 注解的表達能力和靈活性十分有限。盡管我們花了很多時間在調查、設計和試驗上,但最強大的 MyBatis 映射并不能用注解來建構——我們真沒開玩笑。而 ​

​C#​

​​ 屬性就沒有這些限制,是以 ​

​MyBatis.NET​

​ 的配置會比 XML 有更大的選擇餘地。雖說如此,基于 Java 注解的配置還是有它的好處的。

映射注解示例

這個例子展示了如何使用 ​

​@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(column = "cid", javaType = Integer.class, id = true),
  @Arg(column = "name", javaType = String.class)
})
@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 {

  // 如果不使用 @Param,就應該定義與 mapper 方法相同的參數
  public static String buildGetUsersByName(
      final String name, final String orderByColumn) {
    return new SQL(){{
      SELECT("*");
      FROM("users");
      WHERE("name like #{name} || '%'");
      ORDER_BY(orderByColumn);
    }}.toString();
  }

  // 如果使用 @Param,就可以隻定義需要使用的參數
  public static String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) {
    return new SQL(){{
      SELECT("*");
      FROM("users");
      WHERE("name like #{name} || '%'");
      ORDER_BY(orderByColumn);
    }}.toString();
  }
}      

以下例子展示了通過使用全局配置Configuration 來實作所有mapper方法共享一個 sql provider 類(3.5.6後可用):

Configuration configuration = new Configuration();
configuration.setDefaultSqlProviderType(TemplateFilePathProvider.class);
 // Specify an sql provider class for sharing on all mapper methods
// ...
// Can omit the type/value attribute on sql provider annotation
// If omit it, the MyBatis apply the class that specified on defaultSqlProviderType.
public interface UserMapper {

  @SelectProvider // Same with @SelectProvider(TemplateFilePathProvider.class)
  User findUser(int id);

  @InsertProvider // Same with @InsertProvider(TemplateFilePathProvider.class)
  void createUser(User user);

  @UpdateProvider // Same with @UpdateProvider(TemplateFilePathProvider.class)
  void updateUser(User user);

  @DeleteProvider // Same with @DeleteProvider(TemplateFilePathProvider.class)
  void deleteUser(int id);
}      

以下例子展示了 ​

​ProviderMethodResolver​

​(3.5.1 後可用)的預設實作使用方法:

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

// 在你的 provider 類中實作 ProviderMethodResolver 接口
class UserSqlProvider implements ProviderMethodResolver {
  // 預設實作中,會将映射器方法的調用解析到實作的同名方法上
  public static String getUsersByName(final String name) {
    return new SQL(){{
      SELECT("*");
      FROM("users");
      if (name != null) {
        WHERE("name like #{value} || '%'");
      }
      ORDER_BY("id");
    }}.toString();
  }
}      
@Select(value = "SELECT SYS_GUID() FROM dual", databaseId = "oracle") // Use this statement if DatabaseIdProvider provide "oracle"
@Select(value = "SELECT uuid_generate_v4()", databaseId = "postgres") // Use this statement if DatabaseIdProvider provide "postgres"
@Select("SELECT RANDOM_UUID()") // Use this statement if the DatabaseIdProvider not configured or not matches databaseId
String generateId();