1. 程式人生 > >Mybatis之mapper XML 檔案

Mybatis之mapper XML 檔案

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>