Mybatis分頁中遇到的坑2


站在巨人的肩膀上
 
 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:結果查詢

  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>
  1. 定義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:嵌套查詢

  1. 在ProductsMapper.xml中定義select語句
   <select id="selectProductsBycategoryId" resultMap="productsListMap"> select <include refid="Base_Column_List"/> from products where category_id = #{id} </select>
  1. 定義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語句
  1. ProductImagesMapper定義selectByProductId查詢語句
    <select id="selectByProductId" resultMap="BaseResultMap"> SELECT <include refid="Base_Column_List"/> from product_images where product_id = #{productId} </select>

總結

優點

  • 准確分頁

缺點

  • 沒有解決N+1的問題,多條SQL查詢語句,效率太差


免責聲明!

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



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