站在巨人的肩膀上
http://crocutax.com/blog/mybatis-one-to-many-nestes-query-and-page-query
Mybatis一對多嵌套查詢和分頁
需求:根據分類ID查詢分類下所屬的商品集合,每個商品又有一個圖片集合。
類似的需求有很多,比如經典的一個用戶有N個角色,一個角色有N個權限,那么通過用戶的id來查詢角色和權限數據等等。
至於分頁插件,無論是Mybatis-PageHelper還是Mybatis-Plus都可以輔助,這里主要記錄不同查詢方式對分頁的影響。
先展示結果:
{
"code": 0, "msg": "success", "data": { "total": 9, "size": 2, "pages": 5, "current": 1, "records": [ { "id": 1, "code": "1410854032", "name": "Esmeralda Kilback", "categoryId": "1", "originPrice": 359, "price": 103, "sales": 299, "commentCount": 0, "freight": 1, "detail": "這里是商品詳情", "createdAt": "2018-04-09 18:52:05", "updatedAt": "2018-04-24 23:41:49", "images": [ { "id": 40, "productId": "1", "link": "uploads/product/201804/18/78a6e4e4d73bfc64b7aef88a90e7f192.png", "createdAt": "2018-04-09 18:52:05", "updatedAt": "2018-04-18 16:37:09" }, { "id": 41, "productId": "1", "link": "uploads/product/201804/18/fffdccaa36a8475ed3d2c71c2f43cb86.png", "createdAt": "2018-04-09 18:52:05", "updatedAt": "2018-04-18 16:37:09" }, { "id": 301, "productId": "1", "link": "uploads/product/201804/18/68b18cbcb090a94123abd9d729528370.png", "createdAt": "2018-04-18 16:35:56", "updatedAt": "2018-04-18 16:35:56" } ] }, { "id": 8, "code": "1925117917", "name": "Edgardo Osinski", "categoryId": "1", "originPrice": 389, "price": 154, "sales": 199, "commentCount": 0, "freight": 14, "detail": "這里是商品詳情...5052 Kyler Walk Suite 921", "createdAt": "2018-04-09 18:52:05", "updatedAt": "2018-04-09 18:52:05", "images": [ { "id": 58, "productId": "8", "link": "uploads/default.png", "createdAt": "2018-04-09 18:52:05", "updatedAt": "2018-04-09 18:52:05" }, { "id": 59, "productId": "8", "link": "uploads/default2.png", "createdAt": "2018-04-09 18:52:05", "updatedAt": "2018-04-09 18:52:05" }, { "id": 60, "productId": "8", "link": "uploads/default3.png", "createdAt": "2018-04-09 18:52:05", "updatedAt": "2018-04-09 18:52:05" } ] } ] } }
定義模型
Product用於數據庫映射,為了保持其簡潔,其他的二次封裝不在Product里進行,而用繼承的方式。
定義模型ProductVo
,
@Data public class ProductVo extends Product { private List<ProductImage> images; }
方式1:結果查詢
- 在ProductsMapper.xml中定義select語句,一次性將關聯數據全部查詢出來,然后進行結果映射
<select id="selectProductsBycategoryId" resultMap="productsListMap"> select p.id, p.name, p.code, ... i.id images_id, i.product_id images_product_id, ... from products p inner join product_images i on p.id = i.product_id where p.category_id = #{id} </select>
- 定義
productsListMap
結果映射
<resultMap id="productsListMap" type="com.longke.mallb2c.entity.vo.ProductVo" extends="BaseResultMap">--> <collection property="images" columnPrefix="images_" resultMap="com.longke.mallb2c.mapper.ProductImagesMapper.BaseResultMap"/> </resultMap>
注意:
property
就是在ProductVo
中定義的商品圖片集合images
字段- 用到了
columnPrefix
列前綴,只是別名前綴,跟數據庫內的字段無關,這個images_
別名前綴跟select
中定義別名時要保持一致。 - 用到了
extends
繼承已有的BaseResultMap
,不用在這里再重新寫Product
表的每個字段的映射了。mapper.xml自動生成工具都會幫我們生成這個BaseResultMap
,直接繼承即可。 collection
用於一對多查詢,查詢的結果映射直接復用ProductImagesMapper
中定義的BaseResultMap
總結
優點
- 一次性查詢,集中映射,簡單,效率
缺點
- 會將collection中查詢到的條數作為分頁的約束條件,導致分頁數據不准確。
比如想查page=1,limit=10
的數據,本來期望的是查詢出10個商品,然后這10個商品分別再嵌套查詢出自己的商品圖片集合。但是會發現,可能商品只有兩三個,每個下面都帶了自己的商品圖片集合。
原因:
先通過表連接把表記錄關聯進來了,如果有3個商品,關聯圖片表之后每個商品有4條圖片記錄,那么其實這時候雖然只有三個商品,但是這個內存中的臨時表已經有12條記錄了,在語句的最后加上 limit 0,10,其實分頁的時候分的是這12條記錄。最終就會導致最終的映射結果只出現了3個商品,而非我們期望的10個商品。
方式2:嵌套查詢
- 在ProductsMapper.xml中定義select語句
<select id="selectProductsBycategoryId" resultMap="productsListMap"> select <include refid="Base_Column_List"/> from products where category_id = #{id} </select>
- 定義
productsListMap
結果映射
<resultMap id="productsListMap" type="com.longke.mallb2c.entity.vo.ProductVo" extends="BaseResultMap"> <collection property="images" ofType="com.longke.mallb2c.entity.ProductImage" column="{productId=id}" select="com.longke.mallb2c.mapper.ProductImagesMapper.selectByProductId"> </collection> </resultMap>
注意:
column
是參數傳遞,即將Product的哪個屬性傳遞給嵌套的查詢語句,{productId=id}
代表將Product的id
屬性傳遞給參數productId
select
直接使用ProductImagesMapper
中定義的select語句
- 在
ProductImagesMapper
定義selectByProductId
查詢語句
<select id="selectByProductId" resultMap="BaseResultMap"> SELECT <include refid="Base_Column_List"/> from product_images where product_id = #{productId} </select>
總結
優點
- 准確分頁
缺點
- 沒有解決N+1的問題,多條SQL查詢語句,效率太差