MyBatis 的真正強大在於它的映射語句,也是它的魔力所在。因爲它的異常強大,映射器的 XML 文件就顯得相對簡單。若是拿它跟具備相同功能的 JDBC 代碼進行對比,你會當即發現省掉了將近 95% 的代碼。MyBatis 就是針對 SQL 構建的,而且比普通的方法作的更好。html
SQL 映射文件有不多的幾個頂級元素(按照它們應該被定義的順序):java
cache
– 給定命名空間的緩存配置。cache-ref
– 其餘命名空間緩存配置的引用。resultMap
– 是最複雜也是最強大的元素,用來描述如何從數據庫結果集中來加載對象。parameterMap
– 已廢棄!老式風格的參數映射。內聯參數是首選,這個元素可能在未來被移除,這裏不會記錄。sql
– 可被其餘語句引用的可重用語句塊。insert
– 映射插入語句update
– 映射更新語句delete
– 映射刪除語句select
– 映射查詢語句下一部分將從語句自己開始來描述每一個元素的細節。算法
查詢語句是 MyBatis 中最經常使用的元素之一,光能把數據存到數據庫中價值並不大,若是還能從新取出來纔有用,多數應用也都是查詢比修改要頻繁。對每一個插入、更新或刪除操做,一般對應多個查詢操做。這是 MyBatis 的基本原則之一,也是將焦點和努力放到查詢和結果映射的緣由。簡單查詢的 select 元素是很是簡單的。好比:sql
<select id="selectPerson" parameterType="int" resultType="hashmap"> SELECT * FROM PERSON WHERE ID = #{id} </select>
這個語句被稱做 selectPerson,接受一個 int(或 Integer)類型的參數,並返回一個 HashMap 類型的對象,其中的鍵是列名,值即是結果行中的對應值。數據庫
注意參數符號:apache
#{id}
這就告訴 MyBatis 建立一個預處理語句參數,經過 JDBC,這樣的一個參數在 SQL 中會由一個「?」來標識,並被傳遞到一個新的預處理語句中,就像這樣:編程
// Similar JDBC code, NOT MyBatis… String selectPerson = "SELECT * FROM PERSON WHERE ID=?"; PreparedStatement ps = conn.prepareStatement(selectPerson); ps.setInt(1,id);
固然,這須要不少單獨的 JDBC 的代碼來提取結果並將它們映射到對象實例中,這就是 MyBatis 節省你時間的地方。咱們須要深刻了解參數和結果映射,細節部分咱們下面來了解。數組
select 元素有不少屬性容許你配置,來決定每條語句的做用細節。緩存
<select id="selectPerson" parameterType="int" parameterMap="deprecated" resultType="hashmap" resultMap="personResultMap" flushCache="false" useCache="true" timeout="10000" fetchSize="256" statementType="PREPARED" resultSetType="FORWARD_ONLY">
Select Attributes安全
屬性 | 描述 |
---|---|
id | 在命名空間中惟一的標識符,能夠被用來引用這條語句。 |
parameterType | 將會傳入這條語句的參數類的徹底限定名或別名。 這個屬性是可選的,由於 MyBatis 能夠經過 TypeHandler 推斷出具體傳入語句的參數,默認值爲 unset。 |
使用內聯參數映射和 parameterType 屬性。 |
|
resultType | 從這條語句中返回的指望類型的類的徹底限定名或別名。 注意若是是集合情形,那應該是集合能夠包含的類型, 而不能是集合自己。使用 resultType 或 resultMap,但不能同時使用。 |
resultMap | 外部 resultMap 的命名引用。 結果集的映射是 MyBatis 最強大的特性, 對其有一個很好的理解的話,許多複雜映射的情形都能迎刃而解。 使用 resultMap 或 resultType,但不能同時使用。 |
flushCache | 將其設置爲 true,任什麼時候候只要語句被調用, 都會致使本地緩存和二級緩存都會被清空,默認值:false。 |
useCache | 將其設置爲 true,將會致使本條語句的結果被二級緩存, 默認值:對 select 元素爲 true。 |
timeout | 這個設置是在拋出異常以前, 驅動程序等待數據庫返回請求結果的秒數。默認值爲 unset(依賴驅動)。 |
fetchSize | 這是嘗試影響驅動程序每次批量返回的結果行數和這個設置值相等。 默認值爲 unset(依賴驅動)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一個。 這會讓 MyBatis 分別使用 Statement,PreparedStatement 或 CallableStatement, 默認值:PREPARED。 |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一個, 默認值爲 unset (依賴驅動)。 |
databaseId | 若是配置了 databaseIdProvider, MyBatis 會加載全部的不帶 databaseId 或匹配當前 databaseId 的語句; 若是帶或者不帶的語句都有,則不帶的會被忽略。 |
resultOrdered | 這個設置僅針對嵌套結果 select 語句適用: 若是爲 true,就是假設包含了嵌套結果集或是分組了, 這樣的話當返回一個主結果行的時候, 就不會發生有對前面結果集的引用的狀況。 這就使得在獲取嵌套的結果集的時候不至於致使內存不夠用。默認值:false。 |
resultSets | 這個設置僅對多結果集的狀況適用, 它將列出語句執行後返回的結果集並每一個結果集給一個名稱, 名稱是逗號分隔的。 |
數據變動語句 insert,update 和 delete 的實現很是接近:
<insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyProperty="" keyColumn="" useGeneratedKeys="" timeout="20"> <update id="updateAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20"> <delete id="deleteAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">
Insert, Update, Delete 's Attributes
屬性 | 描述 |
---|---|
id | 命名空間中的惟一標識符,可被用來表明這條語句。 |
這個屬性是可選的,由於 MyBatis 能夠經過 TypeHandler 推斷出具體傳入語句的參數,默認值爲 unset。 |
|
parameterMap | 這是引用外部 parameterMap 的已經被廢棄的方法。 使用內聯參數映射和 parameterType 屬性。 |
flushCache | 將其設置爲 true,任什麼時候候只要語句被調用, 都會致使本地緩存和二級緩存都會被清空, 默認值:true(對應插入、更新和刪除語句)。 |
timeout | 這個設置是在拋出異常以前,驅動程序等待數據庫返回請求結果的秒數。 默認值爲 unset(依賴驅動)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一個。 這會讓 MyBatis 分別使用 Statement,PreparedStatement 或 CallableStatement,默認值:PREPARED。 |
useGeneratedKeys | (僅對 insert 和 update 有用)這會令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法來取出由數據庫內部生成的主鍵(好比:像 MySQL 和 SQL Server 這樣的關係數據庫管理系統的自動遞增字段),默認值:false。 |
keyProperty | (僅對 insert 和 update 有用)惟一標記一個屬性, MyBatis 會經過 getGeneratedKeys 的返回值 或者經過 insert 語句的 selectKey 子元素設置它的鍵值, 默認:unset。若是但願獲得多個生成的列,也能夠是逗號分隔的屬性名稱列表。 |
keyColumn | (僅對 insert 和 update 有用)經過生成的鍵值設置表中的列名, 這個設置僅在某些數據庫(像 PostgreSQL)是必須的, 當主鍵列不是表中的第一列的時候須要設置。 若是但願獲得多個生成的列,也能夠是逗號分隔的屬性名稱列表。 |
databaseId | 若是配置了 databaseIdProvider,MyBatis 會加載全部的不帶 databaseId 或匹配當前 databaseId 的語句; 若是帶或者不帶的語句都有,則不帶的會被忽略。 |
下面就是 insert,update 和 delete 語句的示例:
<insert id="insertAuthor"> insert into Author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio}) </insert> <update id="updateAuthor"> update Author set username = #{username}, password = #{password}, email = #{email}, bio = #{bio} where id = #{id} </update> <delete id="deleteAuthor"> delete from Author where id = #{id} </delete>
如前所述,插入語句的配置規則更加豐富,在插入語句裏面有一些額外的屬性和子元素用來處理主鍵的生成,並且有多種生成方式。
首先,若是你的數據庫支持自動生成主鍵的字段(好比 MySQL 和 SQL Server),那麼你能夠設置 useGeneratedKeys=」true」,而後再把 keyProperty 設置到目標屬性上就OK了。例如,若是上面的 Author 表已經對 id 使用了自動生成的列類型,那麼語句能夠修改成:
<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id"> insert into Author (username,password,email,bio) values (#{username},#{password},#{email},#{bio}) </insert>
若是你的數據庫還支持多行插入, 你也能夠傳入一個Authors數組或集合,並返回自動生成的主鍵。
<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id"> insert into Author (username, password, email, bio) values <foreach item="item" collection="list" separator=","> (#{item.username}, #{item.password}, #{item.email}, #{item.bio}) </foreach> </insert>
對於不支持自動生成類型的數據庫或可能不支持自動生成主鍵的 JDBC 驅動,MyBatis 有另一種方法來生成主鍵。
這裏有一個簡單(甚至很傻)的示例,它能夠生成一個隨機 ID(你最好不要這麼作,但這裏展現了 MyBatis 處理問題的靈活性及其所關心的廣度):
<insert id="insertAuthor"> <selectKey keyProperty="id" resultType="int" order="BEFORE"> select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1 </selectKey> insert into Author (id, username, password, email,bio, favourite_section) values (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR}) </insert>
在上面的示例中,selectKey 元素將會首先運行,Author 的 id 會被設置,而後插入語句會被調用。這給你了一個和數據庫中來處理自動生成的主鍵相似的行爲,避免了使 Java 代碼變得複雜。
selectKey 元素描述以下:
<selectKey keyProperty="id" resultType="int" order="BEFORE" statementType="PREPARED">
selectKey 屬性
屬性 | 描述 |
---|---|
keyProperty | selectKey 語句結果應該被設置的目標屬性。 若是但願獲得多個生成的列,也能夠是逗號分隔的屬性名稱列表。 |
keyColumn | 匹配屬性的返回結果集中的列名稱。 若是但願獲得多個生成的列,也能夠是逗號分隔的屬性名稱列表。 |
resultType | 結果的類型。 MyBatis 一般能夠推算出來,可是爲了更加肯定寫上也不會有什麼問題。 MyBatis 容許任何簡單類型用做主鍵的類型,包括字符串。 若是但願做用於多個生成的列,則可使用一個包含指望屬性的 Object 或一個 Map。 |
order | 這能夠被設置爲 BEFORE 或 AFTER。 若是設置爲 BEFORE,那麼它會首先選擇主鍵, 設置 keyProperty 而後執行插入語句。 若是設置爲 AFTER,那麼先執行插入語句,而後是 selectKey 元素 - 這和像 Oracle 的數據庫類似,在插入語句內部可能有嵌入索引調用。 |
statementType | 與前面相同, MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 語句的映射類型, 分別表明 PreparedStatement 和 CallableStatement 類型。 |
這個元素能夠被用來定義可重用的 SQL 代碼段,能夠包含在其餘語句中。它能夠被靜態地(在加載參數) 參數化. 不一樣的屬性值經過包含的實例變化. 好比:
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
這個 SQL 片斷能夠被包含在其餘語句中,例如:
<select id="selectUsers" resultType="map"> select <include refid="userColumns"><property name="alias" value="t1"/></include>, <include refid="userColumns"><property name="alias" value="t2"/></include> from some_table t1 cross join some_table t2 </select>
屬性值也能夠被用在 include 元素的 refid 屬性裏(
<include refid="${include_target}"/>
)或 include 內部語句中(
${prefix}Table
),例如:
<sql id="sometable"> ${prefix}Table </sql> <sql id="someinclude"> from <include refid="${include_target}"/> </sql> <select id="select" resultType="map"> select field1, field2, field3 <include refid="someinclude"> <property name="prefix" value="Some"/> <property name="include_target" value="sometable"/> </include> </select>
前面的全部語句中你所見到的都是簡單參數的例子,實際上參數是 MyBatis 很是強大的元素,對於簡單的作法,大概 90% 的狀況參數都不多,好比:
<select id="selectUsers" resultType="User"> select id, username, password from users where id = #{id} </select>
上面的這個示例說明了一個很是簡單的命名參數映射。參數類型被設置爲 int,這樣這個參數就能夠被設置成任何內容。原生的類型或簡單數據類型(好比整型和字符串)由於沒有相關屬性,它會徹底用參數值來替代。然而,若是傳入一個複雜的對象,行爲就會有一點不一樣了。好比:
<insert id="insertUser" parameterType="User"> insert into users (id, username, password) values (#{id}, #{username}, #{password}) </insert>
若是 User 類型的參數對象傳遞到了語句中,id、username 和 password 屬性將會被查找,而後將它們的值傳入預處理語句的參數中。
這點相對於向語句中傳參是比較好的,並且又簡單,不過參數映射的功能遠不止於此。
首先,像 MyBatis 的其餘部分同樣,參數也能夠指定一個特殊的數據類型。
#{property,javaType=int,jdbcType=NUMERIC}
像 MyBatis 的剩餘部分同樣,javaType 一般能夠由參數對象肯定,除非該對象是一個 HashMap。這時所使用的 TypeHandler
應該明確指明 javaType。
若是一個列容許 null 值,而且會傳遞值 null 的參數,就必需要指定 JDBC Type。閱讀 PreparedStatement.setNull() 的 JavaDocs 文檔來獲取更多信息。
爲了之後定製類型處理方式,你也能夠指定一個特殊的類型處理器類(或別名),好比:
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
儘管看起來配置變得愈來愈繁瑣,但實際上,不多須要去設置它們。
對於數值類型,還有一個小數保留位數的設置,來肯定小數點後保留的位數。
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
最後,mode 屬性容許你指定 IN,OUT 或 INOUT 參數。若是參數爲 OUT 或 INOUT,參數對象屬性的真實值將會被改變,就像你在獲取輸出參數時所指望的那樣。若是 mode 爲 OUT(或 INOUT),並且 jdbcType 爲 CURSOR(也就是 Oracle 的 REFCURSOR),你必須指定一個 resultMap 來映射結果集 ResultMap 到參數類型。要注意這裏的 javaType 屬性是可選的,若是留空而且 jdbcType 是 CURSOR,它會被自動地被設爲 ResultMap。
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}
MyBatis 也支持不少高級的數據類型,好比結構體,可是當註冊 out 參數時你必須告訴它語句類型名稱。好比(再次提示,在實際中要像這樣不能換行):
#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}
儘管全部這些選項很強大,但大多時候你只須簡單地指定屬性名,其餘的事情 MyBatis 會本身去推斷,頂多要爲可能爲空的列指定 jdbcType。
#{firstName} #{middleInitial,jdbcType=VARCHAR} #{lastName}
默認狀況下,使用 #{} 格式的語法會致使 MyBatis 建立 PreparedStatement 參數並安全地設置參數(就像使用 ? 同樣)。這樣作更安全,更迅速,一般也是首選作法,不過有時你就是想直接在 SQL 語句中插入一個不轉義的字符串。好比,像 ORDER BY,你能夠這樣來使用:
ORDER BY ${columnName}
這裏 MyBatis 不會修改或轉義字符串。
用這種方式接受用戶的輸入,並將其用於語句中的參數是不安全的,會致使潛在的 SQL 注入攻擊,所以要麼不容許用戶輸入這些字段,要麼自行轉義並檢驗。
resultMap 元素是 MyBatis 中最重要最強大的元素。它可讓你從 90% 的 JDBC ResultSets 數據提取代碼中解放出來, 並在一些情形下容許你作一些 JDBC 不支持的事情。 實際上,在對複雜語句進行聯合映射的時候,它極可能能夠代替數千行的同等功能的代碼。 ResultMap 的設計思想是,簡單的語句不須要明確的結果映射,而複雜一點的語句只須要描述它們的關係就好了。
你已經見過簡單映射語句的示例了,但沒有明確的 resultMap。好比:
<select id="selectUsers" resultType="map"> select id, username, hashedPassword from some_table where id = #{id} </select>
上述語句只是簡單地將全部的列映射到 HashMap 的鍵上,這由 resultType 屬性指定。 雖然在大部分狀況下都夠用,可是 HashMap 不是一個很好的領域模型。 你的程序更可能會使用 JavaBean 或 POJO(Plain Old Java Objects,普通 Java 對象)做爲領域模型。 MyBatis 對二者都支持。看看下面這個 JavaBean:
package com.someapp.model; public class User { private int id; private String username; private String hashedPassword; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getHashedPassword() { return hashedPassword; } public void setHashedPassword(String hashedPassword) { this.hashedPassword = hashedPassword; } }
基於 JavaBean 的規範,上面這個類有 3 個屬性:id,username 和 hashedPassword。這些屬性會對應到 select 語句中的列名。
這樣的一個 JavaBean 能夠被映射到 ResultSet,就像映射到 HashMap 同樣簡單。
<select id="selectUsers" resultType="com.someapp.model.User"> select id, username, hashedPassword from some_table where id = #{id} </select>
類型別名是你的好幫手。使用它們,你就能夠不用輸入類的徹底限定名稱了。好比:
<!-- In mybatis-config.xml file --> <typeAlias type="com.someapp.model.User" alias="User"/> <!-- In SQL Mapping XML file --> <select id="selectUsers" resultType="User"> select id, username, hashedPassword from some_table where id = #{id} </select>
這些狀況下,MyBatis 會在幕後自動建立一個 ResultMap,再基於屬性名來映射列到 JavaBean 的屬性上。若是列名和屬性名沒有精確匹配,能夠在 SELECT 語句中對列使用別名(這是一個 基本的 SQL 特性)來匹配標籤。好比:
<select id="selectUsers" resultType="User"> select user_id as "id", user_name as "userName", hashed_password as "hashedPassword" from some_table where id = #{id} </select>
ResultMap 最優秀的地方在於,雖然你已經對它至關了解了,可是根本就不須要顯式地用到他們。 上面這些簡單的示例根本不須要下面這些繁瑣的配置。 出於示範的緣由,讓咱們來看看最後一個示例中,若是使用外部的 resultMap 會怎樣,這也是解決列名不匹配的另一種方式。
<resultMap id="userResultMap" type="User"> <id property="id" column="user_id" /> <result property="username" column="user_name"/> <result property="password" column="hashed_password"/> </resultMap>
引用它的語句使用 resultMap 屬性就好了(注意咱們去掉了 resultType 屬性)。好比:
<select id="selectUsers" resultMap="userResultMap"> select user_id, user_name, hashed_password from some_table where id = #{id} </select>
若是世界老是這麼簡單就行了。
MyBatis 建立的一個想法是:數據庫不可能永遠是你所想或所需的那個樣子。 咱們但願每一個數據庫都具有良好的第三範式或 BCNF 範式,惋惜它們不總都是這樣。 若是有一個獨立且完美的數據庫映射模式,全部應用程序均可以使用它,那就太好了,但惋惜也沒有。 ResultMap 就是 MyBatis 對這個問題的答案。
好比,咱們如何映射下面這個語句?
<!-- Very Complex Statement --> <select id="selectBlogDetails" resultMap="detailedBlogResultMap"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio, A.favourite_section as author_favourite_section, P.id as post_id, P.blog_id as post_blog_id, P.author_id as post_author_id, P.created_on as post_created_on, P.section as post_section, P.subject as post_subject, P.draft as draft, P.body as post_body, C.id as comment_id, C.post_id as comment_post_id, C.name as comment_name, C.comment as comment_text, T.id as tag_id, T.name as tag_name from Blog B left outer join Author A on B.author_id = A.id left outer join Post P on B.id = P.blog_id left outer join Comment C on P.id = C.post_id left outer join Post_Tag PT on PT.post_id = P.id left outer join Tag T on PT.tag_id = T.id where B.id = #{id} </select>
你可能想把它映射到一個智能的對象模型,這個對象表示了一篇博客,它由某位做者所寫, 有不少的博文,每篇博文有零或多條的評論和標籤。 咱們來看看下面這個完整的例子,它是一個很是複雜的 ResultMap (假設做者,博客,博文,評論和標籤都是類型的別名)。 不用緊張,咱們會一步一步來講明。 雖然它看起來使人望而生畏,但其實很是簡單。
<!-- 超複雜的 Result Map --> <resultMap id="detailedBlogResultMap" type="Blog"> <constructor> <idArg column="blog_id" javaType="int"/> </constructor> <result property="title" column="blog_title"/> <association property="author" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> <result property="favouriteSection" column="author_favourite_section"/> </association> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <association property="author" javaType="Author"/> <collection property="comments" ofType="Comment"> <id property="id" column="comment_id"/> </collection> <collection property="tags" ofType="Tag" > <id property="id" column="tag_id"/> </collection> <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator> </collection> </resultMap>
resultMap 元素有不少子元素和一個值得討論的結構。 下面是 resultMap 元素的概念視圖。
ResultMap 屬性
屬性 | 描述 |
---|---|
id | 當前命名空間中的一個惟一標識,用於標識一個result map. |
type | 類的徹底限定名, 或者一個類型別名 (內置的別名能夠參考上面的表格). |
autoMapping | 若是設置這個屬性,MyBatis將會爲這個ResultMap開啓或者關閉自動映射。 這個屬性會覆蓋全局的屬性 autoMappingBehavior。默認值爲:unset。 |
最佳實踐 最好一步步地創建結果映射。單元測試能夠在這個過程當中起到很大幫助。若是你嘗試一次建立一個像上面示例那樣的巨大的結果映射, 那麼極可能會出現錯誤並且很難去使用它來完成工做。 從最簡單的形態開始,逐步進化。並且別忘了單元測試!使用框架的缺點是有時候它們看上去像黑盒子(不管源代碼是否可見)。 爲了確保你實現的行爲和想要的一致,最好的選擇是編寫單元測試。提交 bug 的時候它也能起到很大的做用。
下一部分將詳細說明每一個元素。
id & result
<id property="id" column="post_id"/> <result property="subject" column="post_subject"/>
這些是結果映射最基本的內容。id 和 result 都將一個列的值映射到一個簡單數據類型(字符串,整型,雙精度浮點數,日期等)的屬性或字段。
這二者之間的惟一不一樣是, id 表示的結果將是對象的標識屬性,這會在比較對象實例時用到。 這樣能夠提升總體的性能,尤爲是緩存和嵌套結果映射(也就是聯合映射)的時候。
兩個元素都有一些屬性:
Id 和 Result 的屬性
屬性 | 描述 |
---|---|
property | 映射到列結果的字段或屬性。若是用來匹配的 JavaBeans 存在給定名字的屬性,那麼它將會被使用。 不然 MyBatis 將會尋找給定名稱 property 的字段。 不管是哪種情形,你均可以使用一般的點式分隔形式進行復雜屬性導航。 好比,你能夠這樣映射一些簡單的東西: 「username」 , 或者映射到一些複雜的東西: 「address.street.number」 。 |
column | 數據庫中的列名,或者是列的別名。 通常狀況下,這和 傳遞給 resultSet.getString(columnName) 方法的參數同樣。 |
javaType | 一個 Java 類的徹底限定名,或一個類型別名(參考上面內建類型別名 的列表) 。 若是你映射到一個 JavaBean,MyBatis 一般能夠判定類型。 然而,若是你映射到的是 HashMap,那麼你應該明確地指定 javaType 來保證指望的行爲。 |
jdbcType | JDBC 類型,所支持的 JDBC 類型參見這個表格以後的「支持的 JDBC 類型」。 只須要在可能執行插入、更新和刪除的容許空值的列上指定 JDBC 類型。 這是 JDBC 的要求而非 MyBatis 的要求。 若是你直接面向 JDBC 編程,你須要對可能爲 null 的值指定這個類型。 |
typeHandler | 咱們在前面討論過的默認類型處理器。 使用這個屬性,你能夠覆蓋默 認的類型處理器。 這個屬性值是一個類型處理 器實現類的徹底限定名,或者是類型別名。 |
支持的 JDBC 類型
爲了將來的參考,MyBatis 經過包含的 jdbcType 枚舉型,支持下面的 JDBC 類型。
BIT | FLOAT | CHAR | TIMESTAMP | OTHER | UNDEFINED |
TINYINT | REAL | VARCHAR | BINARY | BLOB | NVARCHAR |
SMALLINT | DOUBLE | LONGVARCHAR | VARBINARY | CLOB | NCHAR |
INTEGER | NUMERIC | DATE | LONGVARBINARY | BOOLEAN | NCLOB |
BIGINT | DECIMAL | TIME | NULL | CURSOR | ARRAY |
構造方法
經過修改對象屬性的方式,能夠知足大多數的數據傳輸對象(Data Transfer Object,DTO)以及絕大部分領域模型的要求。 但有些狀況下你想使用不可變類。 一般來講,不多或基本不變的、包含引用或查詢數 據的表,很適合使用不可變類。 構造方法注入容許你在初始化時 爲類設置屬性的值,而不用暴露出公有方法。MyBatis 也支持私有屬性和私有 JavaBeans 屬 性來達到這個目的,但有一些人更青睞於構造方法注入。constructor 元素就是爲此而生的。
public class User { //... public User(Integer id, String username, int age) { //... } //... }
爲了將結果注入構造方法,MyBatis須要經過某種方式定位相應的構造方法。 在下面的例子中,MyBatis搜索一個聲明瞭三個形參的的構造方法,以 java.lang.Integer, java.lang.String and int 的順序排列。
<constructor> <idArg column="id" javaType="int"/> <arg column="username" javaType="String"/> <arg column="age" javaType="_int"/> </constructor>
當你在處理一個帶有多個形參的構造方法時,很容易在保證 arg 元素的正確順序上出錯。 從版本 3.4.3 開始,能夠在指定參數名稱的前提下,以任意順序編寫 arg 元素。 爲了經過名稱來引用構造方法參數,你能夠添加 @Param 註解,或者使用 '-parameters' 編譯選項並啓用 useActualParamName 選項(默認開啓)來編譯項目。 下面的例子對於同一個構造方法依然是有效的,儘管第二和第三個形參順序與構造方法中聲明的順序不匹配。
<constructor> <idArg column="id" javaType="int" name="id" /> <arg column="age" javaType="_int" name="age" /> <arg column="username" javaType="String" name="username" /> </constructor>
若是類中存在名稱和類型相同的屬性,那麼能夠省略 javaType 。
剩餘的屬性和規則和普通的 id 和 result 元素是同樣的。
屬性 | 描述 |
---|---|
column | 數據庫中的列名,或者是列的別名。 通常狀況下,這和 傳遞給 resultSet.getString(columnName) 方法的參數同樣。 |
javaType | 一個 Java 類的徹底限定名,或一個類型別名(參考上面內建類型別名的列表)。 若是你映射到一個 JavaBean,MyBatis 一般能夠判定類型。 然而,如 果你映射到的是 HashMap,那麼你應該明確地指定 javaType 來保證指望的 行爲。 |
jdbcType | JDBC 類型,所支持的 JDBC 類型參見這個表格以前的「支持的 JDBC 類型」。 只須要在可能執行插入、更新和刪除的容許空值的列上指定 JDBC 類型。 這是 JDBC 的要求而非 MyBatis 的要求。 若是你直接面向 JDBC 編程,你須要對可能爲 null 的值指定這個類型。 |
typeHandler | 咱們在前面討論過的默認類型處理器。 使用這個屬性,你能夠覆蓋默 認的類型處理器。 這個屬性值是一個類型處理 器實現類的徹底限定名,或者是類型別名。 |
select | 用於加載複雜類型屬性的映射語句的 ID,它會從 column 屬性中指定的列檢索數據, 做爲參數傳遞給此 select 語句。具體請參考 Association 標籤。 |
resultMap | ResultMap 的 ID,能夠將嵌套的結果集映射到一個合適的對象樹中, 功能和 select 屬性類似,它能夠實現將多表鏈接操做的結果映射成一個單一的ResultSet。 這樣的ResultSet將會將包含重複或部分數據重複的結果集正確的映射到嵌套的對象樹中。 爲了實現它, MyBatis容許你 「串聯」 ResultMap,以便解決嵌套結果集的問題。 想了解更多內容,請參考下面的Association元素。 |
name | 構造方法形參的名字。從3.4.3版本開始,經過指定具體的名字, 你能夠以任意順序寫入arg元素。參看上面的解釋。 |
關聯--一對一
<association property="author" column="blog_author_id" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> </association>
關聯元素處理「有一個」類型的關係。好比,在咱們的示例中,一個博客有一個用戶。 關聯映射就工做於這種結果之上。你指定了目標屬性,來獲取值的列,屬性的 java 類型(很 多狀況下 MyBatis 能夠本身算出來) ,若是須要的話還有 jdbc 類型,若是你想覆蓋或獲取的 結果值還須要類型控制器。
關聯中不一樣的是你須要告訴 MyBatis 如何加載關聯。MyBatis 在這方面會有兩種不一樣的 方式:
屬性 | 描述 |
---|---|
property | 映射到列結果的字段或屬性。 若是用來匹配的 JavaBeans 存在給定名字的屬性,那麼它將會被使用。 不然 MyBatis 將會尋找與給定名稱相同的字段。 這兩種情形你可使用一般點式的複雜屬性導航。 好比,你能夠這樣映射 一 些 東 西 :「 username 」, 或 者 映 射 到 一 些 復 雜 的 東 西 : 「address.street.number」 。 |
javaType | 一個 Java 類的徹底限定名,或一個類型別名(參考上面內建類型別名的列 表) 。 若是你映射到一個 JavaBean,MyBatis 一般能夠判定類型。 然而,如 javaType 果你映射到的是 HashMap,那麼你應該明確地指定 javaType 來保證所需的 行爲。 |
jdbcType | 在這個表格以前的所支持的 JDBC 類型列表中的類型。 JDBC 類型是僅僅 須要對插入, 更新和刪除操做可能爲空的列進行處理。 這是 JDBC 的須要, jdbcType 而不是 MyBatis 的。 若是你直接使用 JDBC 編程,你須要指定這個類型-但 僅僅對可能爲空的值。 |
typeHandler | 咱們在前面討論過默認的類型處理器。 使用這個屬性,你能夠覆蓋默認的 typeHandler 類型處理器。 這個屬性值是類的徹底限定名或者是一個類型處理器的實現, 或者是類型別名。 |
"關聯"的嵌套查詢
屬性 | 描述 |
---|---|
column | 來自數據庫的列名,或重命名的列標籤。 這和一般傳遞給 resultSet.getString(columnName)方法的字符串是相同的。 column 注 意 : 要 處 理 復 合 主 鍵 , 你 可 以 指 定 多 個 列 名 通 過 column= 」 {prop1=col1,prop2=col2} 」 這種語法來傳遞給嵌套查詢語 句。 這會引發 prop1 和 prop2 以參數對象形式來設置給目標嵌套查詢語句。 |
select | 另一個映射語句的 ID,能夠加載這個屬性映射須要的複雜類型。 獲取的 在列屬性中指定的列的值將被傳遞給目標 select 語句做爲參數。 表格後面 有一個詳細的示例。 select 注 意 : 要 處 理 復 合 主 鍵 , 你 可 以 指 定 多 個 列 名 通 過 column= 」 {prop1=col1,prop2=col2} 」 這種語法來傳遞給嵌套查詢語 句。 這會引發 prop1 和 prop2 以參數對象形式來設置給目標嵌套查詢語句。 |
fetchType | 可選的。有效值爲 lazy和eager。 若是使用了,它將取代全局配置參數lazyLoadingEnabled。 |
示例:
<resultMap id="blogResult" type="Blog"> <association property="author" column="author_id" javaType="Author" select="selectAuthor"/> </resultMap> <select id="selectBlog" resultMap="blogResult"> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id="selectAuthor" resultType="Author"> SELECT * FROM AUTHOR WHERE ID = #{id} </select>
咱們有兩個查詢語句:一個來加載博客,另一個來加載做者,並且博客的結果映射描 述了「selectAuthor」語句應該被用來加載它的 author 屬性。
其餘全部的屬性將會被自動加載,假設它們的列和屬性名相匹配。
這種方式很簡單, 可是對於大型數據集合和列表將不會表現很好。 問題就是咱們熟知的 「N+1 查詢問題」。歸納地講,N+1 查詢問題能夠是這樣引發的:
你執行了一個單獨的 SQL 語句來獲取結果列表(就是「+1」)。
對返回的每條記錄,你執行了一個查詢語句來爲每一個加載細節(就是「N」)。
這個問題會致使成百上千的 SQL 語句被執行。這一般不是指望的。
MyBatis 能延遲加載這樣的查詢就是一個好處,所以你能夠分散這些語句同時運行的消 耗。然而,若是你加載一個列表,以後迅速迭代來訪問嵌套的數據,你會調用全部的延遲加 載,這樣的行爲多是很糟糕的。
因此還有另一種方法。
"關聯"的嵌套結果
屬性 | 描述 |
---|---|
resultMap | 這是結果映射的 ID,能夠映射關聯的嵌套結果到一個合適的對象圖中。 這 是一種替代方法來調用另一個查詢語句。 這容許你聯合多個表來合成到 resultMap 一個單獨的結果集。 這樣的結果集可能包含重複,數據的重複組須要被分 解,合理映射到一個嵌套的對象圖。 爲了使它變得容易,MyBatis 讓你「鏈 接」結果映射,來處理嵌套結果。 一個例子會很容易來仿照,這個表格後 面也有一個示例。 |
columnPrefix | 當鏈接多表時,你將不得不使用列別名來避免ResultSet中的重複列名。 指定columnPrefix容許你映射列名到一個外部的結果集中。 請看後面的例子。 |
notNullColumn | 默認狀況下,子對象僅在至少一個列映射到其屬性非空時才建立。 經過對這個屬性指定非空的列將改變默認行爲, 這樣作以後Mybatis將僅在這些列非空時才建立一個子對象。 能夠指定多個列名,使用逗號分隔。默認值:未設置(unset)。 |
autoMapping | 若是使用了,當映射結果到當前屬性時,Mybatis將啓用或者禁用自動映射。 該屬性覆蓋全局的自動映射行爲。 注意它對外部結果集無影響, 因此在select or resultMap屬性中這個是毫無心義的。 默認值:未設置(unset)。 |
在上面你已經看到了一個很是複雜的嵌套關聯的示例。 下面這個是一個很是簡單的示例 來講明它如何工做。代替了執行一個分離的語句,咱們聯合博客表和做者表在一塊兒,就像:
<select id="selectBlog" resultMap="blogResult"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio from Blog B left outer join Author A on B.author_id = A.id where B.id = #{id} </select>
注意這個聯合查詢, 以及採起保護來確保全部結果被惟一併且清晰的名字來重命名。 這使得映射很是簡單。如今咱們能夠映射這個結果:
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <association property="author" column="blog_author_id" javaType="Author" resultMap="authorResult"/> </resultMap> <resultMap id="authorResult" type="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> </resultMap>
在上面的示例中你能夠看到博客的做者關聯表明着「authorResult」結果映射來加載做 者實例。
很是重要: id元素在嵌套結果映射中扮演着非 常重要的角色。你應該老是指定一個或多個能夠惟一標識結果的屬性。實際上若是你不指定它的話, MyBatis仍然能夠工做,可是會有嚴重的性能問題。在能夠惟一標識結果的狀況下, 儘量少的選擇屬性。主鍵是一個顯而易見的選擇(即便是複合主鍵)。
如今,上面的示例用了外部的結果映射元素來映射關聯。這使得 Author 結果映射能夠 重用。然而,若是你不須要重用它的話,或者你僅僅引用你全部的結果映射合到一個單獨描 述的結果映射中。你能夠嵌套結果映射。這裏給出使用這種方式的相同示例:
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <association property="author" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> </association> </resultMap>
上面你已經看到了如何處理「有一個」類型關聯。可是「有不少個」是怎樣的?下面這 個部分就是來討論這個主題的。
集合--一對多
<collection property="posts" ofType="domain.blog.Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </collection>
集合元素的做用幾乎和關聯是相同的。實際上,它們也很類似,文檔的異同是多餘的。 因此咱們更多關注於它們的不一樣。
咱們來繼續上面的示例,一個博客只有一個做者。可是博客有不少文章。在博客類中, 這能夠由下面這樣的寫法來表示:
private List<Post> posts;
要映射嵌套結果集合到 List 中,咱們使用集合元素。就像關聯元素同樣,咱們能夠從 鏈接中使用嵌套查詢,或者嵌套結果。
"集合"的嵌套查詢
首先,讓咱們看看使用嵌套查詢來爲博客加載文章。
<resultMap id="blogResult" type="Blog"> <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/> </resultMap> <select id="selectBlog" resultMap="blogResult"> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id="selectPostsForBlog" resultType="Post"> SELECT * FROM POST WHERE BLOG_ID = #{id} </select>
這裏你應該注意不少東西,但大部分代碼和上面的關聯元素是很是類似的。首先,你應 該注意咱們使用的是集合元素。而後要注意那個新的「ofType」屬性。這個屬性用來區分 JavaBean(或字段)屬性類型和集合包含的類型來講是很重要的。因此你能夠讀出下面這個 映射:
<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
讀做: 「在 Post 類型的 ArrayList 中的 posts 的集合。」
javaType 屬性是不須要的,由於 MyBatis 在不少狀況下會爲你算出來。因此你能夠縮短寫法:
<collection property="posts" column="id" ofType="Post" select="selectPostsForBlog"/>
"集合"的嵌套結果
至此,你能夠猜想集合的嵌套結果是如何來工做的,由於它和關聯徹底相同,除了它應 用了一個「ofType」屬性
首先, 讓咱們看看 SQL:
<select id="selectBlog" resultMap="blogResult"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, P.id as post_id, P.subject as post_subject, P.body as post_body, from Blog B left outer join Post P on B.id = P.blog_id where B.id = #{id} </select>
咱們又一次聯合了博客表和文章表,並且關注於保證特性,結果列標籤的簡單映射。現 在用文章映射集合映射博客,能夠簡單寫爲:
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </collection> </resultMap>
一樣,要記得 id 元素的重要性,若是你不記得了,請閱讀上面的關聯部分。
一樣, 若是你引用更長的形式容許你的結果映射的更多重用, 你可使用下面這個替代 的映射:
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" ofType="Post" resultMap="blogPostResult" columnPrefix="post_"/> </resultMap> <resultMap id="blogPostResult" type="Post"> <id property="id" column="id"/> <result property="subject" column="subject"/> <result property="body" column="body"/> </resultMap>
注意 這個對你所映射的內容沒有深度,廣度或關聯和集合相聯合的限制。當映射它們 時你應該在大腦中保留它們的表現。 你的應用在找到最佳方法前要一直進行的單元測試和性 能測試。好在 myBatis 讓你後來能夠改變想法,而不對你的代碼形成很小(或任何)影響。
高級關聯和集合映射是一個深度的主題。文檔只能給你介紹到這了。加上一點聯繫,你 會很快清楚它們的用法。
鑑別器
<discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator>
有時一個單獨的數據庫查詢也許返回不少不一樣 (可是但願有些關聯) 數據類型的結果集。 鑑別器元素就是被設計來處理這個狀況的, 還有包括類的繼承層次結構。 鑑別器很是容易理 解,由於它的表現很像 Java 語言中的 switch 語句。
定義鑑別器指定了 column 和 javaType 屬性。 列是 MyBatis 查找比較值的地方。 JavaType 是須要被用來保證等價測試的合適類型(儘管字符串在不少情形下都會有用)。好比:
<resultMap id="vehicleResult" type="Vehicle"> <id property="id" column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javaType="int" column="vehicle_type"> <case value="1" resultMap="carResult"/> <case value="2" resultMap="truckResult"/> <case value="3" resultMap="vanResult"/> <case value="4" resultMap="suvResult"/> </discriminator> </resultMap>
在這個示例中, MyBatis 會從結果集中獲得每條記錄, 而後比較它的 vehicle 類型的值。 若是它匹配任何一個鑑別器的實例,那麼就使用這個實例指定的結果映射。換句話說,這樣 作徹底是剩餘的結果映射被忽略(除非它被擴展,這在第二個示例中討論) 。若是沒有任何 一個實例相匹配,那麼 MyBatis 僅僅使用鑑別器塊外定義的結果映射。因此,若是 carResult 按以下聲明:
<resultMap id="carResult" type="Car"> <result property="doorCount" column="door_count" /> </resultMap>
那麼只有 doorCount 屬性會被加載。這步完成後完整地容許鑑別器實例的獨立組,儘管 和父結果映射可能沒有什麼關係。這種狀況下,咱們固然知道 cars 和 vehicles 之間有關係, 如 Car 是一個 Vehicle 實例。所以,咱們想要剩餘的屬性也被加載。咱們設置的結果映射的 簡單改變以下。
<resultMap id="carResult" type="Car" extends="vehicleResult"> <result property="doorCount" column="door_count" /> </resultMap>
如今 vehicleResult 和 carResult 的屬性都會被加載了。
儘管曾經有些人會發現這個外部映射定義會多少有一些使人厭煩之處。 所以還有另一 種語法來作簡潔的映射風格。好比:
<resultMap id="vehicleResult" type="Vehicle"> <id property="id" column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javaType="int" column="vehicle_type"> <case value="1" resultType="carResult"> <result property="doorCount" column="door_count" /> </case> <case value="2" resultType="truckResult"> <result property="boxSize" column="box_size" /> <result property="extendedCab" column="extended_cab" /> </case> <case value="3" resultType="vanResult"> <result property="powerSlidingDoor" column="power_sliding_door" /> </case> <case value="4" resultType="suvResult"> <result property="allWheelDrive" column="all_wheel_drive" /> </case> </discriminator> </resultMap>
要記得 這些都是結果映射, 若是你不指定任何結果, 那麼 MyBatis 將會爲你自動匹配列和屬性。因此這些例子中的大部分是很冗長的,而實際上是不須要的。也就是說,不少數據庫 是很複雜的,咱們不太可能對全部示例都能依靠它。
正如你在前面一節看到的,在簡單的場景下,MyBatis能夠替你自動映射查詢結果。 若是遇到複雜的場景,你須要構建一個result map。 可是在本節你將看到,你也能夠混合使用這兩種策略。 讓咱們到深一點的層面上看看自動映射是怎樣工做的。
當自動映射查詢結果時,MyBatis會獲取sql返回的列名並在java類中查找相同名字的屬性(忽略大小寫)。 這意味着若是Mybatis發現了ID列和id屬性,Mybatis會將ID的值賦給id。
一般數據庫列使用大寫單詞命名,單詞間用下劃線分隔;而java屬性通常遵循駝峯命名法。 爲了在這兩種命名方式之間啓用自動映射,須要將 mapUnderscoreToCamelCase設置爲true。
自動映射甚至在特定的result map下也能工做。在這種狀況下,對於每個result map,全部的ResultSet提供的列, 若是沒有被手工映射,則將被自動映射。自動映射處理完畢後手工映射纔會被處理。 在接下來的例子中, id 和 userName列將被自動映射, hashed_password 列將根據配置映射。
<select id="selectUsers" resultMap="userResultMap"> select user_id as "id", user_name as "userName", hashed_password from some_table where id = #{id} </select>
<resultMap id="userResultMap" type="User"> <result property="password" column="hashed_password"/> </resultMap>
有三種自動映射等級:
<select id="selectBlog" resultMap="blogResult"> select B.id, B.title, A.username, from Blog B left outer join Author A on B.author_id = A.id where B.id = #{id} </select>
<resultMap id="blogResult" type="Blog"> <association property="author" resultMap="authorResult"/> </resultMap> <resultMap id="authorResult" type="Author"> <result property="username" column="author_username"/> </resultMap>
在結果中Blog和Author均將自動映射。可是注意Author有一個id屬性,在ResultSet中有一個列名爲id, 因此Author的id將被填充爲Blog的id,這不是你所期待的。因此須要謹慎使用FULL。
經過添加autoMapping屬性能夠忽略自動映射等級配置,你能夠啓用或者禁用自動映射指定的ResultMap。
<resultMap id="userResultMap" type="User" autoMapping="false"> <result property="password" column="hashed_password"/> </resultMap>
MyBatis 包含一個很是強大的查詢緩存特性,它能夠很是方便地配置和定製。MyBatis 3 中的緩存實現的不少改進都已經實現了,使得它更增強大並且易於配置。
默認狀況下是沒有開啓緩存的,除了局部的 session 緩存,能夠加強變現並且處理循環 依賴也是必須的。要開啓二級緩存,你須要在你的 SQL 映射文件中添加一行:
<cache/>
字面上看就是這樣。這個簡單語句的效果以下:
NOTE The cache will only apply to statements declared in the mapping file where the cache tag is located. If you are using the Java API in conjunction with the XML mapping files, then statements declared in the companion interface will not be cached by default. You will need to refer to the cache region using the @CacheNamespaceRef annotation.
全部的這些屬性均可以經過緩存元素的屬性來修改。好比:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
這個更高級的配置建立了一個 FIFO 緩存,並每隔 60 秒刷新,存數結果對象或列表的 512 個引用,並且返回的對象被認爲是隻讀的,所以在不一樣線程中的調用者之間修改它們會 致使衝突。
可用的收回策略有:
flushInterval(刷新間隔)能夠被設置爲任意的正整數,並且它們表明一個合理的毫秒 形式的時間段。默認狀況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句時刷新。
size(引用數目)能夠被設置爲任意正整數,要記住你緩存的對象數目和你運行環境的 可用內存資源數目。默認值是 1024。
readOnly(只讀)屬性能夠被設置爲 true 或 false。只讀的緩存會給全部調用者返回緩 存對象的相同實例。所以這些對象不能被修改。這提供了很重要的性能優點。可讀寫的緩存 會返回緩存對象的拷貝(經過序列化) 。這會慢一些,可是安全,所以默認是 false。
MyBatis 包含一個很是強大的查詢緩存特性,它能夠很是方便地配置和定製。MyBatis 3 中的緩存實現的不少改進都已經實現了,使得它更增強大並且易於配置。
默認狀況下是沒有開啓緩存的,除了局部的 session 緩存,能夠加強變現並且處理循環 依賴也是必須的。要開啓二級緩存,你須要在你的 SQL 映射文件中添加一行:
除了這些自定義緩存的方式, 你也能夠經過實現你本身的緩存或爲其餘第三方緩存方案 建立適配器來徹底覆蓋緩存行爲。
<cache type="com.domain.something.MyCustomCache"/>
這個示 例展 示了 如何 使用 一個 自定義 的緩 存實 現。type 屬 性指 定的 類必 須實現 org.mybatis.cache.Cache 接口。這個接口是 MyBatis 框架中不少複雜的接口之一,可是簡單 給定它作什麼就行。
public interface Cache { String getId(); int getSize(); void putObject(Object key, Object value); Object getObject(Object key); boolean hasKey(Object key); Object removeObject(Object key); void clear(); }
要配置你的緩存, 簡單和公有的 JavaBeans 屬性來配置你的緩存實現, 並且是經過 cache 元素來傳遞屬性, 好比, 下面代碼會在你的緩存實現中調用一個稱爲 「setCacheFile(String file)」 的方法:
<cache type="com.domain.something.MyCustomCache"> <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/> </cache>
你可使用全部簡單類型做爲 JavaBeans 的屬性,MyBatis 會進行轉換。 And you can specify a placeholder(e.g. ${cache.file}) to replace value defined at configuration properties.
從3.4.2版本開始,MyBatis已經支持在全部屬性設置完畢之後能夠調用一個初始化方法。若是你想要使用這個特性,請在你的自定義緩存類裏實現 org.apache.ibatis.builder.InitializingObject 接口。
public interface InitializingObject { void initialize() throws Exception; }
記得緩存配置和緩存實例是綁定在 SQL 映射文件的命名空間是很重要的。所以,全部 在相同命名空間的語句正如綁定的緩存同樣。 語句能夠修改和緩存交互的方式, 或在語句的 語句的基礎上使用兩種簡單的屬性來徹底排除它們。默認狀況下,語句能夠這樣來配置:
<select ... flushCache="false" useCache="true"/> <insert ... flushCache="true"/> <update ... flushCache="true"/> <delete ... flushCache="true"/>
由於那些是默認的,你明顯不能明確地以這種方式來配置一條語句。相反,若是你想改 變默認的行爲,只能設置 flushCache 和 useCache 屬性。好比,在一些狀況下你也許想排除 從緩存中查詢特定語句結果,或者你也許想要一個查詢語句來刷新緩存。類似地,你也許有 一些更新語句依靠執行而不須要刷新緩存。
回想一下上一節內容, 這個特殊命名空間的惟一緩存會被使用或者刷新相同命名空間內 的語句。也許未來的某個時候,你會想在命名空間中共享相同的緩存配置和實例。在這樣的 狀況下你可使用 cache-ref 元素來引用另一個緩存。
<cache-ref namespace="com.someone.application.data.SomeMapper"/>
本例涉及的知識包括上面的"集合"的嵌套查詢小節。
相對於"集合"的嵌套查詢小節的例子,本例多了一級嵌套。即類A包含類B的集合,而類B中又包含了類C的集合。
直接根據表table_a的ID直接映射返回類A對象。
此外,對應的也可使用"集合"的嵌套結果進行查詢。在此就不給出實例了。
<!--根據ID查詢返回A對象-BEGIN--> <!--一、根據table_a的ID查詢返回A對象--> <!--定義返回的類A的結果集映射--> <!--A中包含3個屬性:String id, String aName, List<B> bList <resultMap type="A" id="AMap"> <id column="ID" jdbcType="VARCHAR" property="id"/> <result column="A_NAME" jdbcType="DECIMAL" property="aName"/> <!--下面的column的值id指的是table_a表的主鍵,在table_b表中是外鍵a_id,在子查詢getBList時用於匹配外鍵a_id--> <collection property="bList" column="id" ofType="B" select="getBList"/> </resultMap> <select id="findA" parameterType="java.lang.String" resultMap="AMap"> select a.* from table_a a where a.ID = #{id} </select> <!--二、查詢B:根據table_b的外鍵a_id查詢與table_a的ID匹配的記錄--> <!--定義返回類B的結果映射--> <!--B中包含3個屬性:String id, String bName, List<C> cList <resultMap type="B" id="BMap"> <id column="ID" jdbcType="VARCHAR" property="id"/> <result column="B_NAME" jdbcType="VARCHAR" property="bName"/> <!--下面的column的值id指的是table_b的主鍵id,在table_c表中是外鍵b_id,在子查詢getCList時用於匹配外鍵b_id--> <collection property="cList" column="id" ofType="C" select="getCList"/> </resultMap> <!--在table_b表中根據外鍵a_id查詢table_b表符合條件的記錄--> <select id="getBList" parameterType="java.lang.String" resultMap="BMap"> select b.* from table_b b where b.a_id = #{id} </select> <!--三、查詢C:根據table_c的外鍵b_id查詢與table_b的ID匹配的記錄--> <!--定義返回類C的結果映射--> <!--B中包含2個屬性:String id, String cName <resultMap id="CMap" type="C"> <id column="ID" jdbcType="VARCHAR" property="id"/> <result column="C_NAME" jdbcType="VARCHAR" property="cName"/> </resultMap> <!--在table_c表中根據外鍵b_id查詢table_c表符合條件的記錄--> <select id="getCList" parameterType="java.lang.String" resultMap="CMap"> select c.* from table_c c where c.b_id =#{id} </select> <!--根據ID查詢返回A對象-END-->
參考資料
Mybatis 級聯查詢 (一對多 )
MyBatis 3官方文檔之SELECT
mybatis映射器配置細則