關於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();
  }
}

以下例子展示瞭如何在註解上使用databaseId (3.5.5後可用):

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

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: