Mybatis之mapper XML 檔案
原文連結:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html
一、mapper XML 檔案
MyBatis 的真正強大在於它的映射語句,也是它的魔力所在。由於它的異常強大,映射器的 XML 檔案就顯得相對簡單。如果拿它跟具有相同功能的 JDBC 代碼進行對比,你會立即發現省掉了將近 95% 的代碼。MyBatis 就是針對 SQL 構建的,並且比普通的方法做的更好。
SQL 對應檔有很少的幾個頂級元素(按照它們應該被定義的順序): cache – 給定命名空間的緩衝配置。 cache-ref – 其他命名空間緩衝配置的引用。 resultMap – 是最複雜也是最強大的元素,用來描述如何從資料庫結果集中來載入對象。 parameterMap – 已廢棄。老式風格的參數映射。內聯參數是首選,這個元素可能在將來被移除,這裡不會記錄。 sql – 可被其他語句引用的可重用語句塊。 insert – 映射插入語句 update – 映射更新語句 delete – 映射刪除語句 select – 映射查詢語句
下一部分將從語句本身開始來描述每個元素的細節。 1.1、select
查詢語句是 MyBatis 中最常用的元素之一,光能把資料存到資料庫中價值並不大,如果還能重新取出來才有用,多數應用也都是查詢比修改要頻繁。對每個插入、更新或刪除操作,通常對應多個查詢操作。這是 MyBatis 的基本原則之一,也是將焦點和努力放到查詢和結果映射的原因。簡單查詢的 select 元素是非常簡單的。比如:
<select id="selectPerson" parameterType="int" resultType="hashmap"> SELECT * FROM PERSON WHERE ID = #{id}</select> 這個語句被稱作 selectPerson,接受一個 int(或 Integer)類型的參數,並返回一個 HashMap 類型的對象,其中的鍵是列名,值便是結果行中的對應值
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">
1.2、insert,update 和 delete
資料變更語句 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 語句的樣本:
<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>
1.3、SQL
這個元素可以被用來定義可重用的 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>
屬性值可以用於包含的refid屬性或者包含的字句裡面的屬性值,例如:
<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>
1.4、ResultMap
resultMap 元素是 MyBatis 中最重要最強大的元素。它就是讓你遠離 90%的需要從結果集中取出資料的 JDBC 代碼的那個東西,而且在一些情形下允許你做一些 JDBC 不支援的事情。事實上,編寫相似於對複雜語句聯合映射這些等同的代碼,也許可以跨過上千行的代碼。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>
進階結果映射 resultMap constructor - 類在執行個體化時,用來注入結果到構造方法中 idArg - ID 參數;標記結果作為 ID 可以協助提高整體效能 arg - 注入到構造方法的一個普通結果 id – 一個 ID 結果;標記結果作為 ID 可以協助提高整體效能 result – 注入到欄位或 JavaBean 屬性的普通結果 association – 一個複雜的類型關聯;許多結果將包成這種類型 嵌入結果映射 – 結果映射自身的關聯,或者參考一個 collection – 複雜類型的集 嵌入結果映射 – 結果映射自身的集,或者參考一個 discriminator – 使用結果值來決定使用哪個結果映射 case – 基於某些值的結果映射 嵌入結果映射 – 這種情形結果也映射它本身,因此可以包含很多相 同的元素,或者它可以參照一個外部的結果映射。 關聯
<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 在這方面會有兩種不同的方式: 巢狀查詢:通過執行另外一個 SQL 映射語句來返回預期的複雜類型。 嵌套結果:使用嵌套結果映射來處理重複的聯合結果的子集。首先,然讓我們來查看這個元素的屬性。所有的你都會看到,它和普通的只由 select 和
resultMap 屬性的結果映射不同。
<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 屬性。
其他所有的屬性將會被自動載入,假設它們的列和屬性名稱相匹配。 集合(一對多映射)
<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"/>
集合的嵌套結果
<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>