SqlSession 實例在 MyBatis 中是非常強大的一個類。SqlSession 實例中有所有執行語句的方法,提交或回滾事務,還有獲取映射器實例。 在 SqlSession 類中有超過 20 個方法,所以將它們分開成易於理解的組合。
語句執行方法:
這些方法被用來執行定義在 SQL 映射的 XML 文件中的 SELECT,INSERT,UPDA E T 和 DELETE 語句。它們都會自行解釋,每一句都使用語句的 ID 屬性和參數對象,參數可以 是原生類型(自動裝箱或包裝類) ,JavaBean,POJO 或 Map。
namespace + id : 即:namespace 定位到唯一的mapper映射文件,id 定位到這個mapper映射文件的指定的sql語句:
<T> T selectOne(String statement, Object parameter) <E> List<E> selectList(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) 。因為並不是所有語句都需 要參數,這些方法都是有不同重載版本的,它們可以不需要參數對象。
<T> T selectOne(String statement) <E> List<E> selectList(String statement) <K,V> Map<K,V> selectMap(String statement, String mapKey) int insert(String statement) int update(String statement) int delete(String statement)
最后,還有查詢方法的三個高級版本,它們允許你限制返回行數的范圍,或者提供自定 義結果控制邏輯,這通常用於大量的數據集合。
<E> List<E> selectList (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 handler) void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler 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 內部創建結果集列表。
它的接口很簡單。
package org.apache.ibatis.session; public interface ResultHandler { void handleResult(ResultContext context); }
ResultContext 參數給你訪問結果對象本身的方法, 大量結果對象被創建, 你可以使用布 爾返回值的 stop()方法來停止 MyBatis 加載更多的結果。
事務控制方法
控制事務范圍有四個方法。 當然, 如果你已經選擇了自動提交或你正在使用外部事務管 理器,這就沒有任何效果了。然而,如果你正在使用 JDBC 事務管理員,由 Connection 實 例來控制,那么這四個方法就會派上用場:
void commit() void commit(boolean force) void rollback() void rollback(boolean force)
默認情況下 MyBatis 不會自動提交事務, 除非它偵測到有插入, 更新或刪除操作改變了 數據庫。如果你已經做出了一些改變而沒有使用這些方法,那么你可以傳遞 true 到 commit 和 rollback 方法來保證它會被提交(注意,你不能在自動提交模式下強制 session,或者使用 了外部事務管理器時) 。很多時候你不用調用 rollback(),因為如果你沒有調用 commit 時 MyBatis 會替你完成。然而,如果你需要更多對多提交和回滾都可能的 session 的細粒度控 制,你可以使用回滾選擇來使它成為可能。
NOTE MyBatis-Spring and MyBatis-Guice provide declarative transaction handling. So if you are using MyBatis with Spring or Guice please refer to their specific manuals.
清理 Session 級的緩存
void clearCache()
SqlSession 實例有一個本地緩存在執行 update,commit,rollback 和 close 時被清理。要 明確地關閉它(獲取打算做更多的工作) ,你可以調用 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(); }
Or, If you are using jdk 1.7+ and MyBatis 3.2+, you can use the try-with-resources statement:
try (SqlSession session = sqlSessionFactory.openSession()) { // following 3 lines pseudocode for "doing some work" session.insert(...); session.update(...); session.delete(...); session.commit(); }
注意 就像 SqlSessionFactory,你可以通過調用 getConfiguration()方法獲得 SqlSession 使用的 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 來構建映射器接口時要保證在合適的命名 空間中有語句。 而且, 唯一的限制就是你不能在兩個繼承關系的接口中有相同的方法簽名 (這 也是不好的想法)。
你可以傳遞多個參數給一個映射器方法。 如果你這樣做了, 默認情況下它們將會以它們 在參數列表中的位置來命名,比如:#{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。 |
| @CacheNamespaceRef | 類 | <cacheRef> | 參照另外一個命名空間的緩存來使用。 屬性:value,應該是一個名空姐的字 符串值(也就是類的完全限定名) 。 |
| @ConstructorArgs | Method | <constructor> | 收集一組結果傳遞給一個劫奪對象的 構造方法。屬性:value,是形式參數 的數組。 |
| @Arg | 方法 |
|
單 獨 的 構 造 方 法 參 數 , 是 ConstructorArgs 集合的一部分。屬性: id,column,javaType,typeHandler。 id 屬性是布爾值, 來標識用於比較的屬 性,和<idArg>XML 元素相似。 |
| @TypeDiscriminator | 方法 | <discriminator> | 一組實例值被用來決定結果映射的表 現。 屬性: column, javaType, jdbcType, typeHandler,cases。cases 屬性就是實 例的數組。 |
| @Case | 方法 | <case> | 單獨實例的值和它對應的映射。屬性: value,type,results。Results 屬性是結 果數組,因此這個注解和實際的 ResultMap 很相似,由下面的 Results 注解指定。 |
| @Results | 方法 | <resultMap> | 結果映射的列表, 包含了一個特別結果 列如何被映射到屬性或字段的詳情。 屬 性:value,是 Result 注解的數組。 |
| @Result | 方法 |
|
在列和屬性或字段之間的單獨結果映 射。屬 性:id,column, property, javaType ,jdbcType ,type Handler, one,many。id 屬性是一個布爾值,表 示了應該被用於比較(和在 XML 映射 中的<id>相似)的屬性。one 屬性是單 獨 的 聯 系, 和 <association> 相 似 , 而 many 屬 性 是 對 集 合 而 言 的 , 和 <collection>相似。 它們這樣命名是為了 避免名稱沖突。 |
| @One | 方法 | <association> | 復雜類型的單獨屬性值映射。屬性: select,已映射語句(也就是映射器方 法)的完全限定名,它可以加載合適類 型的實例。注意:聯合映射在注解 API 中是不支持的。這是因為 Java 注解的 限制,不允許循環引用。 |
| @Many | 方法 | <collection> | A mapping to a collection property of a complex type. Attributes: select, which is the fully qualified name of a mapped statement (i.e. mapper method) that can load a collection of instances of the appropriate types. NOTE You will notice that join mapping is not supported via the Annotations API. This is due to the limitation in Java Annotations that does not allow for circular references. |
| @MapKey | 方法 | 復 雜 類 型 的 集合 屬 性 映射 。 屬 性 : select,是映射語句(也就是映射器方 法)的完全限定名,它可以加載合適類 型的一組實例。注意:聯合映射在 Java 注解中是不支持的。這是因為 Java 注 解的限制,不允許循環引用。 | |
| @Options | 方法 | 映射語句的屬性 | 這個注解提供訪問交換和配置選項的 寬廣范圍, 它們通常在映射語句上作為 屬性出現。 而不是將每條語句注解變復 雜,Options 注解提供連貫清晰的方式 來訪問它們。屬性:useCache=true , flushCache=false , resultSetType=FORWARD_ONLY , statementType=PREPARED , fetchSize=-1 , , timeout=-1 useGeneratedKeys=false , keyProperty=”id”。 理解 Java 注解是很 重要的,因為沒有辦法來指定“null” 作為值。因此,一旦你使用了 Options 注解,語句就受所有默認值的支配。要 注意什么樣的默認值來避免不期望的 行為。 |
|
方法 |
|
這些注解中的每一個代表了執行的真 實 SQL。 它們每一個都使用字符串數組 (或單獨的字符串)。如果傳遞的是字 符串數組, 它們由每個分隔它們的單獨 空間串聯起來。這就當用 Java 代碼構 建 SQL 時避免了“丟失空間”的問題。 然而,如果你喜歡,也歡迎你串聯單獨 的字符串。屬性:value,這是字符串 數組用來組成單獨的 SQL 語句。 |
|
方法 |
|
這些可選的 SQL 注解允許你指定一個 類名和一個方法在執行時來返回運行 允許創建動態 的 SQL。 基於執行的映射語句, MyBatis 會實例化這個類,然后執行由 provider 指定的方法. 這個方法可以選擇性的接 受參數對象作為它的唯一參數, 但是必 須只指定該參數或者沒有參數。屬性: type,method。type 屬性是類的完全限 定名。method 是該類中的那個方法名。 注意: 這節之后是對 SelectBuilder 類的 討論,它可以幫助你以干凈,容於閱讀 的方式來構建動態 SQL。 |
| @Param | Parameter | N/A | 如果你的映射器的方法需要多個參數, 這個注解可以被應用於映射器的方法 參數來給每個參數一個名字。否則,多 參數將會以它們的順序位置來被命名 (不包括任何 RowBounds 參數) 比如。 #{param1} , #{param2} 等 , 這 是 默 認 的 。 使 用 @Param(“person”),參數應該被命名為 #{person}。 |
| @SelectKey | Method | <selectKey> | This annotation duplicates the <selectKey> functionality for methods annotated with @Insert or @InsertProvider. It is ignored for other methods. If you specify a @SelectKey annotation, then MyBatis will ignore any generated key properties set via the @Options annotation, or configuration properties. Attributes: statement an array of strings which is the SQL statement to execute, keyProperty which is the property of the parameter object that will be updated with the new value, before which must be either true or false to denote if the SQL statement should be executed before or after the insert, resultType which is the Java type of the keyProperty, and statementType=PREPARED. |
| @ResultMap | Method | N/A | This annotation is used to provide the id of a <resultMap> element in an XML mapper to a @Select or @SelectProvider annotation. This allows annotated selects to reuse resultmaps that are defined in XML. This annotation will override any @Results or @ConstructorArgs annotation if both are specified on an annotated select. |
| @ResultType | Method | N/A | This annotation is used when using a result handler. In that case, the return type is void so MyBatis must have a way to determine the type of object to construct for each row. If there is an XML result map, use the @ResultMap annotation. If the result type is specified in XML on the <select> element, then no other annotation is necessary. In other cases, use this annotation. For example, if a @Select annotated method will use a result handler, the return type must be void and this annotation (or @ResultMap) is required. This annotation is ignored unless the method return type is void. |
Mapper Annotation Examples
This example shows using the @SelectKey annotation to retrieve a value from a sequence before an insert:
@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);
This example shows using the @SelectKey annotation to retrieve an identity value after an insert:
@Insert("insert into table2 (name) values(#{name})") @SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class) int insertTable2(Name name);
