Mybatis學習—XML映射文件


總結自 Mybatis官方中文文檔

Mapper XML 文件

MyBatis 的真正強大在於它的映射語句,也是它的魔力所在。由於它的異常強大,映射器的 XML 文件就顯得相對簡單。如果拿它跟具有相同功能的 JDBC 代碼進行對比,你會立即發現省掉了將近 95% 的代碼。MyBatis 就是針對 SQL 構建的,並且比普通的方法做的更好。

SQL 映射文件有很少的幾個頂級元素(按照它們應該被定義的順序):

  • cache – 給定命名空間的緩存配置。
  • cache-ref – 其他命名空間緩存配置的引用。
  • resultMap – 是最復雜也是最強大的元素,用來描述如何從數據庫結果集中來加載對象。
  • parameterMap – 已廢棄!老式風格的參數映射。內聯參數是首選,這個元素可能在將來被移除,這里不會記錄。
  • sql – 可被其他語句引用的可重用語句塊。
  • insert – 映射插入語句
  • update – 映射更新語句
  • delete – 映射刪除語句
  • select – 映射查詢語句

下一部分將從語句本身開始來描述每個元素的細節。

select

<select id="selectPerson" parameterType="int" resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id}
</select>

這個語句被稱作 selectPerson,接受一個 int(或 Integer)類型的參數,並返回一個 HashMap 類型的對象,其中的鍵是列名,值便是結果行中的對應值。

注意參數符號:

#{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。
parameterMap 這是引用外部 parameterMap 的已經被廢棄的方法。使用內聯參數映射和 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,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 命名空間中的唯一標識符,可被用來代表這條語句。
parameterType 將要傳入語句的參數的完全限定類名或別名。這個屬性是可選的,因為 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>

Result Maps

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>

如果世界總是這么簡單就好了。

關聯

<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 屬性的結果映射不同。

屬性 描述
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 可選的。有效值為 lazyeager。 如果使用了,它將取代全局配置參數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>

如果blog有一個co-author怎么辦? select語句將看起來這個樣子:

<select id="selectBlog" resultMap="blogResult"> select B.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_bio from Blog B left outer join Author A on B.author_id = A.id left outer join Author CA on B.co_author_id = CA.id where B.id = #{id} </select>

再次調用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>

因為結果中的列名與resultMap中的列名不同。 你需要指定columnPrefix去重用映射co-author結果的resultMap。

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

上面你已經看到了如何處理“有一個”類型關聯。但是“有很多個”是怎樣的?下面這 個部分就是來討論這個主題的。

集合

<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 讓你后來可以改變想法,而不對你的代碼造成很小(或任何)影響。

高級關聯和集合映射是一個深度的主題。文檔只能給你介紹到這了。加上一點聯系,你 會很快清楚它們的用法。

自動映射

正如你在前面一節看到的,在簡單的場景下,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>

有三種自動映射等級:

  • NONE - 禁用自動映射。僅設置手動映射屬性。
  • PARTIAL - 將自動映射結果除了那些有內部定義內嵌結果映射的(joins).
  • FULL - 自動映射所有。

默認值是PARTIAL,這是有原因的。當使用FULL時,自動映射會在處理join結果時執行,並且join取得若干相同行的不同實體數據,因此這可能導致非預期的映射。下面的例子將展示這種風險:

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

在結果中BlogAuthor均將自動映射。但是注意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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM