mybatis_user_guide(4) Mapper XML 文件
【-1】README
1)本文全文總結于?http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html
【0】SQL 映射文件有很少的幾個頂級元素(按照它們應該被定義的順序):
- cache?– 給定命名空間的緩存配置。
- cache-ref?– 其他命名空間緩存配置的引用。
- resultMap?– 是最復雜也是最強大的元素,用來描述如何從數(shù)據(jù)庫結果集中來加載對象。(干貨——resultMap確實很厲害)
- ?parameterMap?– 已廢棄!老式風格的參數(shù)映射。內(nèi)聯(lián)參數(shù)是首選,這個元素可能在將來被移除,這里不會記錄。
- sql?– 可被其他語句引用的可重用語句塊。
- insert?– 映射插入語句
- update?– 映射更新語句
- delete?– 映射刪除語句
- select?– 映射查詢語句
【1】select 語句 1)簡單查詢的 select 元素是非常簡單的。比如: <select id="selectPerson" parameterType="int" resultType="hashmap">SELECT * FROM PERSON WHERE ID = #{id} </select> 對以上代碼的分析(Analysis):這個語句被稱作 selectPerson,接受一個 int(或 Integer)類型的參數(shù),并返回一個 HashMap 類型的對象,其中的鍵是列名,值便是結果行中的對應值。注意參數(shù)符號:#{id}
2)這就告訴 MyBatis 創(chuàng)建一個預處理語句參數(shù),如下所示:
// Similar JDBC code, NOT MyBatis… String selectPerson = "SELECT * FROM PERSON WHERE ID=?"; PreparedStatement ps = conn.prepareStatement(selectPerson); ps.setInt(1,id); 3)我們需要深入了解參數(shù)和結果映射 3.1)select 元素有很多屬性允許你配置,來決定每條語句的作用細節(jié)。 <selectid="selectPerson"parameterType="int"parameterMap="deprecated"resultType="hashmap"resultMap="personResultMap"flushCache="false"useCache="true"timeout="10000"fetchSize="256"statementType="PREPARED"resultSetType="FORWARD_ONLY"> 3.2)select attributes 如下:
【2】insert, update 和 delete <insertid="insertAuthor"parameterType="domain.blog.Author"flushCache="true"statementType="PREPARED"keyProperty=""keyColumn=""useGeneratedKeys=""timeout="20"><updateid="updateAuthor"parameterType="domain.blog.Author"flushCache="true"statementType="PREPARED"timeout="20"><deleteid="deleteAuthor"parameterType="domain.blog.Author"flushCache="true"statementType="PREPARED"timeout="20">
1)下面就是 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 setusername = #{username},password = #{password},email = #{email},bio = #{bio}where id = #{id} </update><delete id="deleteAuthor">delete from Author where id = #{id} </delete> 2)插入語句的配置規(guī)則更加豐富,有多種生成方式 2.1)首先,如果你的數(shù)據(jù)庫支持自動生成主鍵的字段(比如 MySQL 和 SQL Server),那么你可以設置 useGeneratedKeys=”true”,然后再把 keyProperty 設置到目標屬性上就OK了。 <insert id="insertAuthor" useGeneratedKeys="true"keyProperty="id">insert into Author (username,password,email,bio)values (#{username},#{password},#{email},#{bio}) </insert> 2.2)若數(shù)據(jù)庫支持多行插入,你可以傳遞一個數(shù)組或List 集合對象 并檢索出 自動生成的key <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>
【3】sql 1)intro:?這個元素可以被用來定義可重用的 SQL 代碼段,可以包含在其他語句中。它可以被靜態(tài)地(在加載參數(shù)) 參數(shù)化. 不同的屬性值通過包含的實例變化. 比如:?(干貨——定義sql,并對其復用引用) <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql> 1.1)這個 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 t1cross join some_table t2 </select> 1.2)屬性值可以用于包含的refid屬性或者包含的字句里面的屬性值,例如: <sql id="sometable">${prefix}Table </sql><sql id="someinclude">from<include refid="${include_target}"/> </sql><select id="select" resultType="map">selectfield1, field2, field3<include refid="someinclude"><property name="prefix" value="Some"/><property name="include_target" value="sometable"/></include> </select>
【4】參數(shù)(Parameters) 1)參數(shù)映射 <insert id="insertUser" parameterType="User">insert into users (id, username, password)values (#{id}, #{username}, #{password}) </insert> 對以上代碼的分析(Analysis):如果 User 類型的參數(shù)對象傳遞到了語句中,id、username 和 password 屬性將會被查找,然后將它們的值傳入預處理語句的參數(shù)中。
2)這點對于向語句中傳參是比較好的而且又簡單,不過參數(shù)映射的功能遠不止于此。
2.1)首先,像 MyBatis 的其他部分一樣,參數(shù)也可以指定一個特殊的數(shù)據(jù)類型。
#{property,javaType=int,jdbcType=NUMERIC}Attention)?如果 null 被當作值來傳遞,對于所有可能為空的列,JDBC Type 是需要的。你可以自己通過閱讀預處理語句的 setNull() 方法的 JavaDocs 文檔來研究這種情況。
2.2)為了以后定制類型處理方式,你也可以指定一個特殊的類型處理器類(或別名),比如:
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler} 2.3)對于數(shù)值類型,還有一個小數(shù)保留位數(shù)的設置,來確定小數(shù)點后保留的位數(shù)。 #{height,javaType=double,jdbcType=NUMERIC,numericScale=2} 3)最后,mode 屬性允、你指定 IN,OUT 或 INOUT 參數(shù)。如果參數(shù)為 OUT 或 INOUT,參數(shù)對象屬性的真實值將會被改變,就像你在獲取輸出參數(shù)時所期望的那樣。如果 mode 為 OUT(或 INOUT),而且 jdbcType 為 CURSOR(也就是 Oracle 的 REFCURSOR),你必須指定一個 resultMap 來映射結果集到參數(shù)類型。要注意這里的 javaType 屬性是可選的,如果左邊的空白是 jdbcType 的 CURSOR 類型,它會自動地被設置為結果集。 #{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}
4)字符串替換:默認情況下,使用#{}格式的語法會導致 MyBatis 創(chuàng)建預處理語句屬性并安全地設置值(比如?)。這樣做更安全,更迅速,通常也是首選做法,不過有時你只是想直接在 SQL 語句中插入一個不改變的字符串。比如,像 ORDER BY,你可以這樣來使用:
ORDER BY ${columnName} Attention)?以這種方式接受從用戶輸出的內(nèi)容并提供給語句中不變的字符串是不安全的,會導致潛在的 SQL 注入攻擊,因此要么不允許用戶輸入這些字段,要么自行轉義并檢驗。(干貨——引入了SQL注入攻擊)
【5】Result Maps
1)intro:?ResultMap 的設計就是簡單語句不需要明確的結果映射,而很多復雜語句確實需要描述它們 的關系。
2)看個荔枝:?簡單映射語句的示例了,但沒有明確的 resultMap。比如:
<select id="selectUsers" resultType="map">select id, username, hashedPasswordfrom some_tablewhere id = #{id} </select> 對以上代碼的分析(Analysis):這樣一個語句簡單作用于所有列被自動映射到 HashMap 的鍵上,這由 resultType 屬性 指定。
3)problem + solution:
3.1)problem:這在很多情況下是有用的,但是 HashMap 不能很好描述一個領域模型。
3.2)solution:那樣你的應 用程序將會使用 JavaBeans 或 POJOs(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;} } 3.2.1)這樣的一個 JavaBean 可以被映射到結果集,就像映射到 HashMap 一樣簡單。
<select id="selectUsers" resultType="com.someapp.model.User">select id, username, hashedPasswordfrom some_tablewhere id = #{id} </select> 3.2.2)要記住類型別名是你的伙伴。使用它們你可以不用輸入類的全路徑。比如:(干貨——設定別名) <!-- 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, hashedPasswordfrom some_tablewhere id = #{id} </select>
4)MyBatis 會在幕后自動創(chuàng)建一個 ResultMap,基于屬性名來映射列到 JavaBean 的屬性上。
5)problem+solution:
5.1)problem:如果列名沒有精確匹配;
5.2)solution:(2 solutions)
5.2.1)你可以在列名上使用 select 字句的別名(一個 基本的 SQL 特性)來匹配標簽。比如:
<select id="selectUsers" resultType="User">selectuser_id as "id",user_name as "userName",hashed_password as "hashedPassword"from some_tablewhere id = #{id} </select> 5.2.2)讓我們來看看最后一個示例中 外部的 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> 5.2.2.1)引用它的語句使用 resultMap 屬性就行了(注意我們?nèi)サ袅?resultType 屬性)。比如: <selectid="selectUsers"resultMap="userResultMap">select user_id, user_name, hashed_passwordfrom some_tablewhere id = #{id} </select>【5.1】高級結果映射
1)problem+solutions:1.1)problem:MyBatis 創(chuàng)建的一個想法:數(shù)據(jù)庫不用永遠是你想要的或需要它們是什么樣的。而我們 最喜歡的數(shù)據(jù)庫最好是第三范式或 BCNF 模式,但它們有時不是。如果可能有一個單獨的 數(shù)據(jù)庫映射,所有應用程序都可以使用它,這是非常好的,但有時也不是; 1.2)solutions:結果映射就是 MyBatis 提供處理這個問題的答案; 2)problem:+solutions: 2.1)problem:我們?nèi)绾斡成湎旅孢@個語句? <!-- Very Complex Statement --> <select id="selectBlogDetails" resultMap="detailedBlogResultMap">selectB.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_namefrom Blog Bleft outer join Author A on B.author_id = A.idleft outer join Post P on B.id = P.blog_idleft outer join Comment C on P.id = C.post_idleft outer join Post_Tag PT on PT.post_id = P.idleft outer join Tag T on PT.tag_id = T.idwhere B.id = #{id} </select>
2.2)solutions:下面是一個完整的復雜結果映射例子 (假設作者, 博客, 博文, 評論和標簽都是類型的別名) 我們來看看, 。 但是不用緊張, 我們會一步一步來說明。
<!-- Very Complex 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>
對以上代碼的分析(Analysis):
A1)resultMap:
- constructor?- 類在實例化時,用來注入結果到構造方法中
- idArg?-ID 參數(shù);標記結果作為 ID 可以幫助提高整體效能
- arg?-注入到構造方法的一個普通結果
- id?–一個 ID 結果;標記結果作為 ID 可以幫助提高整體效能
- result?–注入到字段或 JavaBean 屬性的普通結果
- association?–一個復雜的類型關聯(lián);許多結果將包成這種類型
- 嵌入結果映射 – 結果映射自身的關聯(lián),或者參考一個
- collection?–復雜類型的集
- 嵌入結果映射 – 結果映射自身的集,或者參考一個
- discriminator?–使用結果值來決定使用哪個結果映射
- case?–基于某些值的結果映射
- 嵌入結果映射 – 這種情形結果也映射它本身,因此可以包含很多相 同的元素,或者它可以參照一個外部的結果映射。
- case?–基于某些值的結果映射
【5.2】id & result <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> 對以上代碼的分析(Analysis):?這些是結果映射最基本內(nèi)容。id 和 result 都映射一個單獨列的值到簡單數(shù)據(jù)類型(字符 串,整型,雙精度浮點數(shù),日期等)的單獨屬性或字段。
這兩者之間的唯一不同是 id 表示的結果將是當比較對象實例時用到的標識屬性。這幫 助來改進整體表現(xiàn),特別是緩存和嵌入結果映射(也就是聯(lián)合映射) 。
1)id 和 result 屬性list 如下所示:【5.3】支持的 JDBC 類型
【5.4】構造方法 <constructor><idArg column="id" javaType="int"/><arg column="username" javaType="String"/> </constructor> 對以上代碼的分析(Analysis):?idArg是為了 和 id 元素 區(qū)分開; 1)看看下面這個構造函數(shù): public class User {//...public User(int id, String username) {//...} //... } 對以上代碼的分析(Analysis):
A1)當創(chuàng)建一個構造方法元素時,保證參數(shù)是按順序 排列的,而且數(shù)據(jù)類型也是確定的。
A2)該元素的屬性值 和 result 和 id 元素的屬性值相同;
【5.5】關聯(lián) <association property="author" column="blog_author_id" javaType="Author"><id property="id" column="author_id"/><result property="username" column="author_username"/> </association> 1)intro: 關聯(lián)即表關聯(lián); 2)關聯(lián)中不同的是你需要告訴 MyBatis 如何加載關聯(lián)。MyBatis 在這方面會有兩種不同的 方式:
way1)嵌套查詢:通過執(zhí)行另外一個 SQL 映射語句來返回預期的復雜類型。 way2)嵌套結果:使用嵌套結果映射來處理重復的聯(lián)合結果的子集。首先, 讓我們來查看這個元素的屬性。所有的你都會看到,它和普通的只由 select 和?resultMap 屬性的結果映射不同。
【5.6】關聯(lián)的嵌套查詢
1)看個荔枝
<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> 對以上代碼的分析(Analysis):?我們有兩個查詢語句:一個來加載博客,另外一個來加載作者,而且博客的結果映射描 述了“selectAuthor”語句應該被用來加載它的 author 屬性。
其他所有的屬性將會被自動加載,假設它們的列和屬性名相匹配。
這種方式很簡單, 但是對于大型數(shù)據(jù)集合和列表將不會表現(xiàn)很好。 問題就是我們熟知的 “N+1 查詢問題”。概括地講,N+1 查詢問題可以是這樣引起的:(干貨——引入了N+1 查詢問題)
- 你執(zhí)行了一個單獨的 SQL 語句來獲取結果列表(就是“+1”)。
- 對返回的每條記錄,你執(zhí)行了一個查詢語句來為每個加載細節(jié)(就是“N”)。
這個問題會導致成百上千的 SQL 語句被執(zhí)行。這通常不是期望的。
MyBatis 能延遲加載這樣的查詢就是一個好處,因此你可以分散這些語句同時運行的消 耗。然而,如果你加載一個列表,之后迅速迭代來訪問嵌套的數(shù)據(jù),你會調用所有的延遲加 載,這樣的行為可能是很糟糕的。
所以還有另外一種方法。(干貨——引入了 延遲加載)
【5.7】關聯(lián)的嵌套結果
1)看個荔枝:這個是一個非常簡單的示例 來說明它如何工作。代替了執(zhí)行一個分離的語句,我們聯(lián)合博客表和作者表在一起,就像: <selectid="selectBlog"resultMap="blogResult">selectB.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_biofrom Blog B left outer join Author A on B.author_id = A.idwhere B.id = #{id} </select> 1.1)現(xiàn)在我們可以映射這個結果: <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><span style="color: rgb(0, 0, 136); font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 16px; line-height: 24px; background-color: rgb(245, 245, 245);"> </span>
1.2)現(xiàn)在,上面的示例用了外部的結果映射元素來映射關聯(lián)。這使得 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>
2)如果這個博客是由多個作者(聯(lián)合作者)撰寫的話,查詢語句select 映射為這個樣子: <selectid="selectBlog"resultMap="blogResult">selectB.id as blog_id,B.title as blog_title,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,CA.id as co_author_id,CA.username as co_author_username,CA.password as co_author_password,CA.email as co_author_email,CA.bio as co_author_biofrom Blog Bleft outer join Author A on B.author_id = A.idleft outer join Author CA on B.co_author_id = CA.idwhere B.id = #{id} </select> 2.1)調用作者 Author 的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> 2.2)因為結果集中的 列名 不同于 resultMap 中的 列名,你需要指定 columnPrefix 以復用resultMap來映射 聯(lián)合作者結果; <resultMap id="blogResult" type="Blog"><id property="id" column="blog_id" /><result property="title" column="blog_title"/><association property="author"resultMap="authorResult" /><association property="coAuthor"resultMap="authorResult"columnPrefix="co_" /> </resultMap>
【5.8】集合 <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> 1)看個荔枝:?我們來繼續(xù)上面的示例,一個博客只有一個作者。但是博客有很多文章。在博客類中, 這可以由下面這樣的寫法來表示:? private List<Post> posts; 【5.8.1】集合的嵌套查詢 1)intro:?使用嵌套查詢來為博客加載文章; <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="Blog">SELECT * FROM POST WHERE BLOG_ID = #{id} </select>
對以上代碼的分析(Analysis): A1)首先,你應 該注意我們使用的是集合元素。然后要注意那個新的“ofType”屬性。這個屬性用來區(qū)分 JavaBean(或字段)屬性類型和集合包含的類型來說是很重要的。所以你可以讀出下面這個 映射: <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/> 讀作: “在 Post 類型的 ArrayList 中的 posts 的集合。”
A2)javaType 屬性是不需要的,因為 MyBatis 在很多情況下會為你算出來。所以你可以縮短 寫法: <collection property="posts" column="id" ofType="Post" select="selectPostsForBlog"/> 【5.8.2】集合的嵌套結果 1)至此,你可以猜測集合的嵌套結果是如何來工作的,因為它和關聯(lián)完全相同,除了它應 用了一個“ofType”屬性 2)看個荔枝: <select id="selectBlog" resultMap="blogResult">selectB.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 Bleft outer join Post P on B.id = P.blog_idwhere B.id = #{id} </select>
2.1)我們又一次聯(lián)合了博客表和文章表,而且關注于保證特性,結果列標簽的簡單映射。現(xiàn) 在用文章映射集合映射博客,可以簡單寫為:
<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>
2.2)同樣,要記得 id 元素的重要性,如果你不記得了,請閱讀上面的關聯(lián)部分。
2.3)同樣, 如果你引用更長的形式允許你的結果映射的更多重用, 你可以使用下面這個替代 的映射:
<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>
【5.9】鑒別器(switch in java) <discriminator javaType="int" column="draft"><case value="1" resultType="DraftPost"/> </discriminator> 1)intro:??鑒別器非常容易理 解,因為它的表現(xiàn)很像 Java 語言中的 switch 語句。 2)看個荔枝:?定義鑒別器指定了 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> 對以上代碼的分析(Analysis):
A1)?在這個示例中, MyBatis 會從結果集中得到每條記錄, 然后比較它的 vehicle 類型的值。如果它匹配任何一個鑒別器的實例,那么就使用這個實例指定的結果映射。
A2)如果沒有任何 一個實例相匹配,那么 MyBatis 僅僅使用鑒別器塊外定義的結果映射。所以,如果 carResult 按如下聲明: <resultMap id="carResult" type="Car"><result property="doorCount" column="door_count" /> </resultMap>
A3)那么只有 doorCount 屬性會被加載。這步完成后完整地允許鑒別器實例的獨立組,盡管 和父結果映射可能沒有什么關系。這種情況下,我們當然知道 cars 和 vehicles 之間有關系, 如 Car 是一個 Vehicle 實例。因此,我們想要剩余的屬性也被加載。我們設置的結果映射的 簡單改變?nèi)缦隆?/span>(干貨——想要剩余的屬性也被加載,使用繼承extend) <resultMap id="carResult" type="Car" extends="vehicleResult"><result property="doorCount" column="door_count" /> </resultMap> A4)現(xiàn)在 vehicleResult 和 carResult 的屬性都會被加載了。
3)盡管曾經(jīng)有些人會發(fā)現(xiàn)這個外部映射定義會多少有一些令人厭煩之處。 因此還有另外一 種語法來做簡潔的映射風格。比如:
<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>
【6】自動映射
1)intro:?通常數(shù)據(jù)庫列使用大寫單詞命名,單詞間用下劃線分隔;而java屬性一般遵循駝峰命名法。 為了在這兩種命名方式之間啟用自動映射,需要將?mapUnderscoreToCamelCase設置為true。
2)自動映射甚至在特定的result map下也能工作。在這種情況下,對于每一個result map,所有的ResultSet提供的列, 如果沒有被手工映射,則將被自動映射。自動映射處理完畢后手工映射才會被處理。 在接下來的例子中,?id?和?userName列將被自動映射,?hashed_password?列將根據(jù)配置映射。(干貨——自動映射處理完畢后手工映射才會被處理)
3)看個荔枝
<selectid="selectUsers"resultMap="userResultMap">selectuser_id as "id",user_name as "userName",hashed_passwordfrom some_tablewhere id = #{id} </select><resultMap id="userResultMap" type="User"><result property="password" column="hashed_password"/> </resultMap> 4)There are three auto-mapping levels:(3個自動映射級別)
- NONE?- disables auto-mapping. Only manually mapped properties will be set.
- PARTIAL?- will auto-map results except those that have nested result mappings defined inside (joins).(默認級別)
- FULL?- auto-maps everything.
4.1)將PARTIAL 設置為默認級別的原因:The default value is?PARTIAL, and it is so for a reason. When?FULL?is used auto-mapping will be performed when processing join results and joins retrieve data of several different entities in the same row hence this may result in undesired mappings.?
4.2)To understand the risk have a look at the following sample:
<selectid="selectBlog"resultMap="blogResult">selectB.id,B.title,A.username,from Blog B left outer join Author A on B.author_id = A.idwhere 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>
Analysis of the sample) ?With this result map both?Blog?and?Author?will be auto-mapped. But note that?Author?has an?id?property and there is a column named?id?in the ResultSet so Author's id will be filled with Blog's id, and that is not what you were expecting. So use the?FULL?option with caution.
4.3)Regardless of the auto-mapping level configured you can enable or disable the automapping for an specific ResultMap by adding the attribute?autoMapping?to it:?(干貨——添加 autoMapping屬性)
<pre name="code" class="html"><resultMap id="userResultMap" type="User" autoMapping="false"><result property="password" column="hashed_password"/> </resultMap>
【7】緩存
1)intro:?默認情況下是沒有開啟緩存的,除了局部的 session 緩存,可以增強變現(xiàn)而且處理循環(huán) 依賴也是必須的。要開啟二級緩存,你需要在你的 SQL 映射文件中添加一行:
<cache/> 對以上代碼的分析(Analysis):?這個簡單語句的效果如下:
- 映射語句文件中的所有 select 語句將會被緩存。
- 映射語句文件中的所有 insert,update 和 delete 語句會刷新緩存。
- 緩存會使用 Least Recently Used(LRU,最近最少使用的)算法來收回。
- 根據(jù)時間表(比如 no Flush Interval,沒有刷新間隔), 緩存不會以任何時間順序 來刷新。
- 緩存會存儲列表集合或對象(無論查詢方法返回什么)的 1024 個引用。
- 緩存會被視為是 read/write(可讀/可寫)的緩存,意味著對象檢索不是共享的,而 且可以安全地被調用者修改,而不干擾其他調用者或線程所做的潛在修改。
2)?所有的這些屬性都可以通過緩存元素的屬性來修改。比如: <pre name="code" class="html"><cacheeviction="FIFO"flushInterval="60000"size="512"readOnly="true"/> 對以上代碼的分析(Analysis):?這個更高級的配置創(chuàng)建了一個 FIFO 緩存,并每隔 60 秒刷新,存數(shù)結果對象或列表的 512 個引用,而且返回的對象被認為是只讀的,因此在不同線程中的調用者之間修改它們會 導致沖突。
3)可用的收回策略有:
- LRU?– 最近最少使用的:移除最長時間不被使用的對象。(默認策略)
- FIFO?– 先進先出:按對象進入緩存的順序來移除它們。
- SOFT?– 軟引用:移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對象。
- WEAK?– 弱引用:?更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對象。
Supplement)
supplement1)flushInterval(刷新間隔):可以被設置為任意的正整數(shù),而且它們代表一個合理的毫秒 形式的時間段。默認情況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句時刷新。
supplement2)size(引用數(shù)目):可以被設置為任意正整數(shù),要記住你緩存的對象數(shù)目和你運行環(huán)境的 可用內(nèi)存資源數(shù)目。默認值是 1024。
supplement3)readOnly(只讀)屬性: 可以被設置為 true 或 false。只讀的緩存會給所有調用者返回緩 存對象的相同實例。因此這些對象不能被修改。這提供了很重要的性能優(yōu)勢。可讀寫的緩存 會返回緩存對象的拷貝(通過序列化) 。這會慢一些,但是安全,因此默認是 false。
【7.1】使用自定義緩存
1)intro:?你也可以通過實現(xiàn)你自己的緩存或為其他第三方緩存方案 創(chuàng)建適配器來完全覆蓋緩存行為。(干貨——自定義緩存)
<cache type="com.domain.something.MyCustomCache"/>
對以上代碼的分析(Analysis):
A1)type 屬 性指 定的 類必 須實現(xiàn) org.mybatis.cache.Cache 接口。
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(); } 2)看個荔枝 :要配置你的緩存, 簡單和公有的 JavaBeans 屬性來配置你的緩存實現(xiàn), 而且是通過 cache 元素來傳遞屬性,比如, 下面代碼會在你的緩存實現(xiàn)中調用一個稱為 “setCacheFile(String file)” 的方法: <cache type="com.domain.something.MyCustomCache"><property name="cacheFile" value="/tmp/my-custom-cache.tmp"/> </cache>
3)記得緩存配置和緩存實例是綁定在 SQL 映射文件的命名空間是很重要的。因此,所有 在相同命名空間的語句正如綁定的緩存一樣。 語句可以修改和緩存交互的方式, 或在語句的 語句的基礎上使用兩種簡單的屬性來完全排除它們。默認情況下,語句可以這樣來配置:?
<select ... flushCache="false" useCache="true"/> <insert ... flushCache="true"/> <update ... flushCache="true"/> <delete ... flushCache="true"/>
對以上代碼的分析(Analysis) : 因為那些是默認的,你明顯不能明確地以這種方式來配置一條語句。相反,如果你想改 變默認的行為,只能設置 flushCache 和 useCache 屬性。比如,在一些情況下你也許想排除 從緩存中查詢特定語句結果,或者你也許想要一個查詢語句來刷新緩存。相似地,你也許有 一些更新語句依靠執(zhí)行而不需要刷新緩存。(干貨——如果你想改 變默認的行為,只能設置 flushCache 和 useCache 屬性)
【7.2】參照緩存
1)回想一下上一節(jié)內(nèi)容, 這個特殊命名空間的唯一緩存會被使用或者刷新相同命名空間內(nèi) 的語句。也許將來的某個時候,你會想在命名空間中共享相同的緩存配置和實例。在這樣的 情況下你可以使用 cache-ref 元素來引用另外一個緩存。
cache-refnamespace="com.someone.application.data.SomeMapper"/>總結
以上是生活随笔為你收集整理的mybatis_user_guide(4) Mapper XML 文件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 漫步者TWS1耳机怎么配对
- 下一篇: 企业微信头像怎么换