MyBatis之Mapper XML 文件詳解(三)-Result Maps

resultMap 元素是 MyBatis 中最重要最強大的元素。它就是讓你遠離 90%的需要從結果 集中取出數據的 JDBC 代碼的那個東西, 而且在一些情形下允許你做一些 JDBC 不支持的事 情。 事實上, 編寫相似於對複雜語句聯合映射這些等同的代碼, 也許可以跨過上千行的代碼。 

ResultMap 的設計就是簡單語句不需要明確的結果映射,而很多複雜語句確實需要描述它們 的關係。 


你已經看到簡單映射語句的示例了,但沒有明確的 resultMap。比如:

<select id="selectUsers" resultType="map">
 select id, username, hashedPassword
 from some_table
 where id = #{id}
</select>

這樣一個語句簡單作用於所有列被自動映射到 HashMap 的鍵上,這由 resultType 屬性 指定。這在很多情況下是有用的,但是 HashMap 不能很好描述一個領域模型。那樣你的應 用程序將會使用 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;
 }
}

基於 JavaBean 的規範,上面這個類有 3 個屬性:id,username 和 hashedPassword。這些 在 select 語句中會精確匹配到列名。 


這樣的一個 JavaBean 可以被映射到結果集,就像映射到 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 模式,但它們有時不是。如果可能有一個單獨的 數據庫映射,所有應用程序都可以使用它,這是非常好的,但有時也不是。結果映射就是 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>

你可能想把它映射到一個智能的對象模型,包含一個作者寫的博客,有很多的博文,每 篇博文有零條或多條的評論和標籤。 下面是一個完整的複雜結果映射例子 (假設作者, 博客, 博文, 評論和標籤都是類型的別名) 我們來看看, 。 但是不用緊張, 我們會一步一步來說明。 當天最初它看起來令人生畏,但實際上非常簡單。

<!-- 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>

resultMap 元素有很多子元素和一個值得討論的結構。 下面是 resultMap 元素的概念視圖 
resultMap
constructor - 類在實例化時,用來注入結果到構造方法中 
    idArg - ID 參數;標記結果作爲 ID 可以幫助提高整體效能
    arg- 注入到構造方法的一個普通結果
id – 一個 ID 結果;標記結果作爲 ID 可以幫助提高整體效能
result – 注入到字段或 JavaBean 屬性的普通結果
association – 一個複雜的類型關聯;許多結果將包成這種類型 
    嵌入結果映射 – 結果映射自身的關聯,或者參考一個 
collection – 複雜類型的集 
    嵌入結果映射 – 結果映射自身的集,或者參考一個
discriminator – 使用結果值來決定使用哪個結果映射 
    case – 基於某些值的結果映射 
        嵌入結果映射 – 這種情形結果也映射它本身,因此可以包含很多相 同的元素,或者它可以參照一個外部的結果映射。 

ResultMap Attributes

id:當前命名空間中的一個唯一標識,用於標識一個result map.
type:類的全限定名, 或者一個類型別名 (內置的別名可以參考上面的表格). 
autoMapping:如果設置這個屬性,MyBatis將會爲這個ResultMap開啓或者關閉自動映射。這個屬性會覆蓋全局的屬性autoMappingBehavior。默認值爲:unset。 


最佳實踐 通常逐步建立結果映射。單元測試的真正幫助在這裏。如果你嘗試創建 一次創建一個向上面示例那樣的巨大的結果映射, 那麼可能會有錯誤而且很難去控制它 來工作。開始簡單一些,一步一步的發展。而且要進行單元測試!使用該框架的缺點是 它們有時是黑盒(是否可見源代碼) 。你確定你實現想要的行爲的最好選擇是編寫單元 測試。它也可以你幫助得到提交時的錯誤。 


下面一部分將詳細說明每個元素。 
id & result

<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>

這些是結果映射最基本內容。id 和 result 都映射一個單獨列的值到簡單數據類型(字符 串,整型,雙精度浮點數,日期等)的單獨屬性或字段。 


這兩者之間的唯一不同是 id 表示的結果將是當比較對象實例時用到的標識屬性。這幫 助來改進整體表現,特別是緩存和嵌入結果映射(也就是聯合映射) 。 


每個都有一些屬性: 
Id and Result Attributes

property:映射到列結果的字段或屬性。如果匹配的是存在的,和給定名稱相同 的 JavaBeans 的屬性,那麼就會使用。否則 MyBatis 將會尋找給定名稱 property 的字段。這兩種情形你可以使用通常點式的複雜屬性導航。比如,你 可以這樣映射一些東西: “username” ,或者映射到一些複雜的東西: “address.street.number” 。 
column:從數據庫中得到的列名,或者是列名的重命名標籤。這也是通常和會 傳遞給 resultSet.getString(columnName)方法參數中相同的字符串。 
javaType:一個 Java 類的完全限定名,或一個類型別名(參考上面內建類型別名 的列表) 。如果你映射到一個 JavaBean,MyBatis 通常可以斷定類型。 然而,如果你映射到的是 HashMap,那麼你應該明確地指定 javaType 來保證所需的行爲。 
jdbcType:在這個表格之後的所支持的 JDBC 類型列表中的類型。JDBC 類型是僅 僅需要對插入,更新和刪除操作可能爲空的列進行處理。這是 JDBC jdbcType 的需要,而不是 MyBatis 的。如果你直接使用 JDBC 編程,你需要指定 這個類型-但僅僅對可能爲空的值。 
typeHandler:我們在前面討論過默認的類型處理器。使用這個屬性,你可以覆蓋默 認的類型處理器。這個屬性值是類的完全限定名或者是一個類型處理 器的實現,或者是類型別名。


關注微信公衆號:IT哈哈(it_haha),學習更多知識。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章