1. 程式人生 > >resultMap之collection聚集兩種實現方式

resultMap之collection聚集兩種實現方式

最近做得專案用到了MyBatis處理一對多的對映關係,下面的兩個方法中用到了集合的巢狀查詢方法,下面仔細學習一下這兩種方式

聚集元素用來處理“一對多”的關係。需要指定對映的Java實體類的屬性,屬性的javaType(一般為ArrayList);列表中物件的型別ofType(Java實體類);對應的資料庫表的列名稱;
不同情況需要告訴MyBatis 如何載入一個聚集。MyBatis 可以用兩種方式載入:

  • select: 執行一個其它對映的SQL 語句返回一個Java實體型別。較靈活但會將執行多次巢狀的SQL語句。

  • resultMap: 使用一個巢狀的結果對映來處理通過join查詢結果集,對映成Java實體型別。

兩種載入方式格式如下:

集合的巢狀查詢(select)

<collection property="Java屬性名" ofType="另一Java類名" javaType="ArrayList" column="關聯主鍵ID(用於巢狀查詢SQL語句傳入引數,多個用逗號分開)" select="另一個select對映SQL的ID"/>

<select parameterType="int" resultType="另一Java類名" id="另一個select對映SQL的ID">
	SQL語句
</select>
<resultMap id=
"blogResult" type="Blog"> <collection property="posts" javaType=”ArrayList” column="blog_id" ofType="Post" select="selectPostsForBlog"/> </resultMap> <select id="selectBlog" parameterType="int" resultMap="blogResult"> SELECT * FROM BLOG WHERE ID = #{id} </select>
<select id="selectPostsForBlog" parameterType="int" resultType="Author"> SELECT * FROM POST WHERE BLOG_ID = #{id} </select>

注意:column屬性的值必須與相應的SQL查詢語句中的列名相同。MyBatis會將第一條SQL語句查詢出來的該列的值用於所聚集的SQL對映語句的入參。因第一條SQL語句查詢出來的每個該列的值都將用於執行另一個SQL語句,所以聚集的SQL語句將被多次執行

雖然這個方法簡單,但是對於大資料集或列表查詢,就不盡如人意了。這個問題被稱為“N+1 選擇問題”(N+1 Selects Problem)。概括地說,N+1選擇問題是這樣產生的:

您執行單條SQL語句去獲取一個列表的記錄( “+1”)。

對列表中的每一條記錄,再執行一個聯合select 語句來載入每條記錄更加詳細的資訊(“N”)。

這個問題會導致成千上萬的SQL語句的執行,因此並非總是可取的。

上面的例子,MyBatis可以使用延遲載入這些查詢,因此這些查詢立馬可節省開銷。然而,如果您載入一個列表後立即迭代訪問巢狀的資料,這將會呼叫所有的延遲載入,因此效能會變得非常糟糕。

鑑於此,這有另外一種方式。

集合的巢狀結果集(Nested Results for Collection)

<resultMap id="resultMap的ID"  type="Java類名">
	<collection property="Java屬性名" ofType="另一Java類名" javaType="ArrayList" resultMap="另一resultMap的ID"/>
</resultMap>
  
<resultMap="另一resultMap的ID" type="另一Java類名">
	<id property="id" column="關聯主鍵ID"/>
	....
</resultMap>
<select id="selectBlog" parameterType="int" 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>

同樣,我們把Blog和Post兩張表連線在一起,並且也保證列標籤名在對映的時候是唯一且無歧義的。現在將Blog和Post的集合對映在一起是多麼簡單:

<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"/>  
</resultMap>  
   
<resultMap id="blogPostResult" type="Post">  
	<id property="id" column="post_id"/>  
	<result property="subject" column="post_subject"/>  
	<result property="body" column="post_body"/>  
</resultMap>  

注意:column屬性的值必須與相應的SQL查詢語句的列名一樣。