Mybatis - 一對一(association)和一對多(collection)

<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 類型,若是你想覆蓋或獲取的 結果值還須要類型控制器。html

關聯中不一樣的是你須要告訴 MyBatis 如何加載關聯。MyBatis 在這方面會有兩種不一樣的 方式:java

  • 嵌套查詢:經過執行另一個 SQL 映射語句來返回預期的複雜類型。
  • 嵌套結果:使用嵌套結果映射來處理重複的聯合結果的子集。首先,然讓咱們來查看這個元素的屬性。全部的你都會看到,它和普通的只由 select 和

resultMap 屬性的結果映射不一樣。數據庫

屬性 描述
property 映射到列結果的字段或屬性。若是匹配的是存在的,和給定名稱相同的 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 Optional. Valid values are lazy and eager. If present, it supersedes the global configuration parameter lazyLoadingEnabled for this mapping.

示例:編程

<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 屬性。app

其餘全部的屬性將會被自動加載,假設它們的列和屬性名相匹配。less

這種方式很簡單, 可是對於大型數據集合和列表將不會表現很好。 問題就是咱們熟知的 「N+1 查詢問題」。歸納地講,N+1 查詢問題能夠是這樣引發的:dom

  • 你執行了一個單獨的 SQL 語句來獲取結果列表(就是「+1」)。
  • 對返回的每條記錄,你執行了一個查詢語句來爲每一個加載細節(就是「N」)。

這個問題會致使成百上千的 SQL 語句被執行。這一般不是指望的。ide

MyBatis 能延遲加載這樣的查詢就是一個好處,所以你能夠分散這些語句同時運行的消 耗。然而,若是你加載一個列表,以後迅速迭代來訪問嵌套的數據,你會調用全部的延遲加 載,這樣的行爲多是很糟糕的。post

因此還有另一種方法。性能

關聯的嵌套結果

屬性 描述
resultMap 這是結果映射的 ID,能夠映射關聯的嵌套結果到一個合適的對象圖中。這 是一種替代方法來調用另一個查詢語句。這容許你聯合多個表來合成到 resultMap 一個單獨的結果集。這樣的結果集可能包含重複,數據的重複組須要被分 解,合理映射到一個嵌套的對象圖。爲了使它變得容易,MyBatis 讓你「鏈 接」結果映射,來處理嵌套結果。一個例子會很容易來仿照,這個表格後 面也有一個示例。
columnPrefix When joining multiple tables, you would have to use column alias to avoid duplicated column names in the ResultSet. Specifying columnPrefix allows you to map such columns to an external resultMap. Please see the example explained later in this section.
notNullColumn By default a child object is created only if at least one of the columns mapped to the child's properties is non null. With this attribute you can change this behaviour by specifiying which columns must have a value so MyBatis will create a child object only if any of those columns is not null. Multiple column names can be specified using a comma as a separator. Default value: unset.
autoMapping If present, MyBatis will enable or disable auto-mapping when mapping the result to this property. This attribute overrides the global autoMappingBehavior. Note that it has no effect on an external resultMap, so it is pointless to use it with select or resultMap attribute. Default value: 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>

What if the blog has a co-author? The select statement would look like:

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

Recall that the resultMap for Author is defined as follows.

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

Because the column names in the results differ from the columns defined in the resultMap, you need to specify columnPrefix to reuse the resultMap for mapping co-author results.

<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="Blog">
  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」屬性

First, let's look at the 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 讓你後來能夠改變想法,而不對你的代碼形成很小(或任何)影響。

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

大神的總結:http://www.cnblogs.com/xdp-gacl/p/4264440.html

相關文章
相關標籤/搜索