對於MyBatis的學習而言,最好去MyBatis的官方文檔:http://www.mybatis.org/mybatis-3/zh/index.htmlhtml
既然你已經知道如何配置 MyBatis 和建立映射文件,你就已經準備好來提高技能了。 MyBatis 的 Java API 就是你收穫你所作的努力的地方。正如你即將看到的,和 JDBC 相比, MyBatis 很大程度簡化了你的代碼並且保持簡潔,很容易理解和維護。MyBatis 3 已經引入 了不少重要的改進來使得 SQL 映射更加優秀。java
在咱們深刻 Java API 以前,理解關於目錄結構的最佳實踐是很重要的。MyBatis 很是靈 活, 你能夠用你本身的文件來作幾乎全部的事情。 可是對於任一框架, 都有一些最佳的方式。web
讓咱們看一下典型應用的目錄結構:sql
/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
Remember, these are preferences, not requirements, but others will thank you for using a common directory structure.數據庫
這部份內容剩餘的示例將假設你使用了這種目錄結構。apache
使用 MyBatis 的主要 Java 接口就是 SqlSession。儘管你可使用這個接口執行命令,獲 取映射器和管理事務。咱們會討論 SqlSession 自己更多,可是首先咱們仍是要了解若是獲取 一個 SqlSession 實例。SqlSessions 是由 SqlSessionFactory 實例建立的。SqlSessionFactory 對 象 包 含 創 建 SqlSession 實 例 的 所 有 方 法 。 而 SqlSessionFactory 本 身 是 由 SqlSessionFactoryBuilder 建立的,它能夠從 XML 配置,註解或手動配置 Java 來建立 SqlSessionFactory。編程
NOTE When using MyBatis with a dependency injection framework like Spring or Guice, SqlSessions are created and injected by the DI framework so you don't need to use the SqlSessionFactoryBuilder or SqlSessionFactory and can go directly to the SqlSession section. Please refer to the MyBatis-Spring or MyBatis-Guice manuals for further info.數組
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 決定加載哪一種環境,包括數據源和事務管理器。好比:markdown
<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 將會按找下面的順序來加載它們:
所以,最高優先級的屬性是經過方法參數傳遞的,以後是 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.mybatis.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 映射(不推薦使用,由於應用正接收請求) 。configuration 類有全部配置的開關, 這些你已經瞭解了,只在 Java API 中露出來。這裏有一個簡單的示例,如何手動配置 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);
如今你有一個 SqlSessionFactory,能夠用來建立 SqlSession 實例。
SqlSessionFactory 有六個方法能夠用來建立 SqlSession 實例。一般來講,如何決定是你 選擇下面這些方法時:
重載的 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:
這些方法大均可以自我解釋的。 開啓自動提交, "true" 傳遞 給可選的 autoCommit 參數。 提供自定義的鏈接,傳遞一個 Connection 實例給 connection 參數。注意沒有覆蓋同時設置 Connection 和 autoCommit 二者的方法,由於 MyBatis 會使用當前 connection 對象提供的設 置。 MyBatis 爲事務隔離級別調用使用一個 Java 枚舉包裝器, 稱爲 TransactionIsolationLevel, 不然它們按預期的方式來工做,並有 JDBC 支持的 5 級 ( NONE,READ_UNCOMMITTED,READ_COMMITTED,REPEA TABLE_READ,SERIALIZA BLE)
還有一個可能對你來講是新見到的參數,就是 ExecutorType。這個枚舉類型定義了 3 個 值:
ExecutorType.SIMPLE
: 這個執行器類型不作特殊的事情。它爲每一個語句的執行建立一個新的預處理語句。ExecutorType.REUSE
: 這個執行器類型會複用預處理語句。ExecutorType.BATCH
: 這個執行器會批量執行全部更新語句,若是 SELECT 在它們中間執行還會標定它們是 必須的,來保證一個簡單並易於理解的行爲。注意 在 SqlSessionFactory 中還有一個方法咱們沒有說起,就是 getConfiguration()。這 個方法會返回一個 Configuration 實例,在運行時你可使用它來自檢 MyBatis 的配置。
注意 若是你已經使用以前版本 MyBatis,你要回憶那些 session,transaction 和 batch 都是分離的。如今和以往不一樣了,這些都包含在 session 的範圍內了。你須要處理分開處理 事務或批量操做來獲得它們的效果。
如上面所提到的,SqlSession 實例在 MyBatis 中是很是強大的一個類。在這裏你會發現 全部執行語句的方法,提交或回滾事務,還有獲取映射器實例。
在 SqlSession 類中有超過 20 個方法,因此將它們分開成易於理解的組合。
這些方法被用來執行定義在 SQL 映射的 XML 文件中的 SELECT,INSERT,UPDA E T 和 DELETE 語句。它們都會自行解釋,每一句都使用語句的 ID 屬性和參數對象,參數能夠 是原生類型(自動裝箱或包裝類) ,JavaBean,POJO 或 Map。
T selectOne(String statement, Object parameter) List selectList(String statement, Object parameter) Map 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 selectOne(String statement) List selectList(String statement) Map 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<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 內部建立結果集列表。
它的接口很簡單。
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.
void clearCache()
SqlSession 實例有一個本地緩存在執行 update,commit,rollback 和 close 時被清理。要 明確地關閉它(獲取打算作更多的工做) ,你能夠調用 clearCache()。
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 getMapper(Class type)
上述的各個 insert,update,delete 和 select 方法都很強大,但也有些繁瑣,沒有類型安 全,對於你的 IDE 也沒有幫助,還有可能的單元測試。在上面的入門章節中咱們已經看到 了一個使用映射器的示例。
所以, 一個更通用的方式來執行映射語句是使用映射器類。 一個映射器類就是一個簡單 的接口,其中的方法定義匹配於 SqlSession 方法。下面的示例展現了一些方法簽名和它們是 如何映射到 SqlSession 的。
public interface AuthorMapper { // (Author) selectOne("selectAuthor",5); Author selectAuthor(int id); // (List) selectList("selectAuthors") List selectAuthors(); // (Map) selectMap("selectAuthors", "id") @MapKey("id") Map 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 |
類 |
`` | 爲給定的命名空間 (好比類) 配置緩存。 屬性:implemetation,eviction, flushInterval,size 和 readWrite。 |
@CacheNamespaceRef |
類 |
`` | 參照另一個命名空間的緩存來使用。 屬性:value,應該是一個名空間的字 符串值(也就是類的徹底限定名) 。 |
@ConstructorArgs |
Method |
`` | 收集一組結果傳遞給一個劫奪對象的 構造方法。屬性:value,是形式參數 的數組。 |
@Arg |
方法 |
單 獨 的 構 造 方 法 參 數 , 是 ConstructorArgs 集合的一部分。屬性: id,column,javaType,typeHandler。 id 屬性是布爾值, 來標識用於比較的屬 性,和XML 元素類似。 | |
@TypeDiscriminator |
方法 |
`` | 一組實例值被用來決定結果映射的表 現。 屬性: column, javaType, jdbcType, typeHandler,cases。cases 屬性就是實 例的數組。 |
@Case |
方法 |
`` | 單獨實例的值和它對應的映射。屬性: value,type,results。Results 屬性是結 果數組,所以這個註解和實際的 ResultMap 很類似,由下面的 Results 註解指定。 |
@Results |
方法 |
`` | 結果映射的列表, 包含了一個特別結果 列如何被映射到屬性或字段的詳情。 屬 性:value,是 Result 註解的數組。 |
@Result |
方法 |
在列和屬性或字段之間的單獨結果映 射。屬 性:id,column, property, javaType ,jdbcType ,type Handler, one,many。id 屬性是一個布爾值,表 示了應該被用於比較(和在 XML 映射 中的類似)的屬性。one 屬性是單 獨 的 聯 系, 和 相 似 , 而 many 屬 性 是 對 集 合 而 言 的 , 和 類似。 它們這樣命名是爲了 避免名稱衝突。 | |
@One |
方法 |
` | 複雜類型的單獨屬性值映射。屬性: select,已映射語句(也就是映射器方 法)的徹底限定名,它能夠加載合適類 型的實例。注意:聯合映射在註解 API 中是不支持的。這是由於 Java 註解的 限制,不容許循環引用。 fetchType, which supersedes the global configuration parameter lazyLoadingEnabled` for this mapping. |
|
@Many |
方法 |
` | 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, fetchType, which supersedes the global configuration parameter lazyLoadingEnabled` for this mapping. 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 註解,語句就受全部默認值的支配。要 注意什麼樣的默認值來避免不指望的 行爲。 |
* @Insert ,@Update , @Delete , @Select |
方法 |
<insert> ,<update> ,<delete> ,<select> |
這些註解中的每個表明了執行的真 實 SQL。 它們每個都使用字符串數組 (或單獨的字符串)。若是傳遞的是字 符串數組, 它們由每一個分隔它們的單獨 空間串聯起來。這就當用 Java 代碼構 建 SQL 時避免了「丟失空間」的問題。 然而,若是你喜歡,也歡迎你串聯單獨 的字符串。屬性:value,這是字符串 數組用來組成單獨的 SQL 語句。 |
@InsertProvider,@UpdateProvider,@DeleteProvider,@SelectProvider | 方法 | <insert> ,<update> ,<delete> ,<select> |
這些可選的 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 ` functionality for methods annotated with @Insert, @InsertProvider, @Updateor @UpdateProvider. It is ignored for other methods. If you specify a @SelectKeyannotation, then MyBatis will ignore any generated key properties set via the @Optionsannotation, or configuration properties. Attributes: statement an array of strings which is the SQL statement to execute, keyPropertywhich is the property of the parameter object that will be updated with the new value, before which must be either trueor falseto denote if the SQL statement should be executed before or after the insert, resultTypewhich is the Java type of the keyProperty, and statementType=PREPARED`. |
@ResultMap |
Method |
N/A | This annotation is used to provide the id of a ` element in an XML mapper to a @Selector @SelectProviderannotation. This allows annotated selects to reuse resultmaps that are defined in XML. This annotation will override any @Resultsor @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 ` Night Mode |
這個例子展現瞭如何使用 @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);