首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

MyBatis 關聯查詢實戰:嵌套查詢 vs 嵌套結果映射深度對比

     編輯:Java 相關知識

MyBatis 多表關聯查詢完全指南:一對多、多對一、多對多映射實戰

MyBatis 強大的結果映射機制,能將複雜的多表關聯查詢結果自動裝配為豐富的 Java 對象圖。本文通過「博客系統」實戰案例,深度對比嵌套查詢與嵌套結果兩大核心方案,解析 N+1 問題、性能權衡與最佳實踐。

業務場景:博客系統領域模型


// 表結構對應
Author (作者) 1 ——< N Blog (博客)
Blog (博客) 1 ——< N Post (文章)
Post (文章) 1 ——< N Comment (評論)
Post (文章) N ——> M Tag (標簽)  // 中間表 Post_Tag

目標對象圖


public class BlogInfo {
    private Long blogId;
    private String title;
    private Author author;           // 多對一
    private List posts;        // 一對多
    // 每個 Post 內含 List、List
}

方案一:嵌套查詢—— 可複用、但有 N+1 風險

ResultMap 配置



    
    
    
    
    
    
    
    




Service 調用


BlogInfo blogInfo = sqlSession.selectOne("BlogMapper.queryBlogInfoById", blogId);
// 一行代碼獲得完整對象圖

執行流程

  1. 執行主查詢 queryBlogInfoById,獲取 Blog 基礎 ResultSet
  2. 遍歷 ResultSet,按 ResultMap 構建 BlogInfo 對象
  3. 遇到 <association>,執行關聯的 select 查詢作者
  4. 遇到 <collection>,執行關聯的 select 查詢文章列表
  5. 文章對象內若有進一步關聯,遞歸執行

優點

  • SQL 可複用:AuthorMapper.selectByPrimaryKey、PostMapper.selectByBlogId 可獨立被其他業務調用
  • 主查詢簡單:無複雜 JOIN,單表查詢性能穩定
  • 映射配置清晰:關係聲明式,易理解維護

致命缺點:N+1 問題


// 假設查詢 1 個 Blog,關聯 1 個 Author、10 個 Post
// 實際執行 SQL 數 = 1 (主查詢) + 1 (Author) + 10 (Post) = 12 條
// 若查詢 100 個 Blog,每個 10 篇文章 → 1 + 100*1 + 100*10 = 1101 條 SQL!

N+1 公式:總 SQL 數 = 1 (主查詢) + N × (關聯查詢數)。N 為主查詢結果集行數。

適用場景

  • 主查詢結果集極小(如單條查詢、分頁前 10 條)
  • 關聯查詢高度可複用,獨立業務價值大
  • 可接受額外 DB 往返,或配合緩存(二級緩存命中關聯查詢)

方案二:嵌套結果—— 一次查詢、手動映射、性能最優

ResultMap 配置(手動映射所有列)



    
    
    
    
    
        
        
        
        
        
    
    
    
    
        
        
        
        
        
        
    




Service 調用


BlogInfo blogInfo = sqlSession.selectOne("BlogMapper.queryAllBlogInfo");
// 僅 1 條 SQL,內存中完成對象圖組裝

執行流程

  1. 執行聯表查詢,獲取扁平化大 ResultSet(含重複 Blog/Author 數據)
  2. MyBatis 內存中按 <id> 分組:相同 blog_id 歸為同一 BlogInfo
  3. 對每組數據,按 ResultMap 映射構建對象:
  4. 多對一(<association>):按 column 值分組,構建單一 Author
  5. 一對多(<collection>):按 column 值分組,構建 List

優點

  • 僅 1 條 SQL:無 N+1 問題,DB 壓力最小,網絡往返最少
  • 大數據量查詢性能優勢巨大
  • 事務一致性:單次查詢獲取快照視圖

缺點

  • SQL 複雜:多表 JOIN、列別名衝突處理、分頁困難
  • 映射冗長:需手動列出所有列、配置完整 ResultMap
  • 數據冗餘傳輸:Blog/Author 列隨 Post 行重複傳輸(網絡開銷)
  • 不支援延遲加載:一次性全量加載

深度對比與選型決策

維度嵌套查詢嵌套結果
SQL 數量1 + N × 關聯數1
DB 壓力高(多次往返)低(單次)
網絡傳輸精確(按需)冗餘(重複列)
SQL 複雜度低(單表)高(JOIN)
映射配置簡潔(引用 select)冗長(全列映射)
SQL 可複用性高低
延遲加載支援支援不支援
分頁友好度高低(需子查詢/窗口函數)
適用數據量小(N < 20)大(N > 100)

進階實戰:多對多映射(Post ↔ Tag)

嵌套查詢方式



    
    
    
    




嵌套結果方式



    
    
    
        
        
    



最佳實踐建議

  1. 單條/少量查詢 + 高複用關聯 → 嵌套查詢,配合二級緩存緩解 N+1
  2. 列表/大量查詢 + 固定關聯 → 嵌套結果,一次 SQL 解決
  3. 分頁場景 → 主查詢分頁 + 嵌套查詢關聯,或子查詢/窗口函數嵌套結果
  4. 混合策略:主對象嵌套結果,擴展屬性嵌套查詢(按需加載)
  5. 避免:在循環中調用嵌套查詢方法(手動製造 N+1)

實戰案例:模塊樹狀結構(遞歸關聯)


public class Module {
    private int id;
    private String name;
    private Module parentModule;        // 多對一:父模塊
    private List childrenModules; // 一對多:子模塊
}


    
    
    
    
    
    





注意:遞歸關聯極易產生指數級 SQL,生產環境必須配合 fetchType="lazy" 延遲加載,或改用嵌套結果單次查詢構建樹(CTE 遞歸查詢)。

MyBatis 關聯映射功能強大,但「大權在手,大責在身」。理解 N+1 本質、權衡 SQL 複雜度與對象圖豐富度、根據業務訪問模式選型,才能寫出高性能、可維護的持久層代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved