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

JPA 抓取策略深度解析:FetchType.LAZY vs EAGER 實戰對比

     編輯:Java 相關知識

JPA 預設採用 LAZY(延遲載入),實際存取時才發送 SQL。本文以 Garage(車庫)與 Auto(汽車)一對多關聯為例,實測兩種策略的 SQL 差異與效能影響。

實體定義


// Garage.java
@Entity
public class Garage {
    @Id @GeneratedValue
    private Integer gid;
    @Column(length=20)
    private String garagenum;

    @OneToMany(cascade=CascadeType.PERSIST, mappedBy="garage")
    private Set<Auto> autos = new HashSet<>();
    // getter/setter/addGarageAuto...
}

// Auto.java
@Entity
public class Auto {
    @Id @GeneratedValue
    private Integer autoId;
    private String autotype, autonum;

    @ManyToOne
    @JoinColumn(name="garageid")
    private Garage garage;
    // getter/setter...
}

測試代碼


@Test
public void query() {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa-hibernate");
    EntityManager em = emf.createEntityManager();

    Garage garage = em.find(Garage.class, 1); // 載入 ID=1 的車庫

    em.close();
    emf.close();
}

策略 1:預設 LAZY(未標註 fetch)

執行 SQL:


Hibernate: select garage0_.gid as gid1_0_, garage0_.garagenum as garagenum1_0_ 
           from Garage garage0_ where garage0_.gid=?

**僅查詢 Garage**,autos 集合為 Proxy,未觸發關聯查詢。若後續呼叫 garage.getAutos() 再發送第二條 SQL。

策略 2:改為 EAGER(立即載入)


@OneToMany(cascade=CascadeType.PERSIST, fetch=FetchType.EAGER, mappedBy="garage")
public Set<Auto> getAutos() { return autos; }

執行 SQL:


Hibernate: select garage0_.gid as gid1_1_, garage0_.garagenum as garagenum1_1_, 
           autos1_.garageid as garageid3_, autos1_.autoId as autoId3_, 
           autos1_.autoId as autoId0_0_, autos1_.autonum as autonum0_0_, 
           autos1_.autotype as autotype0_0_, autos1_.garageid as garageid0_0_ 
           from Garage garage0_ 
           left outer join Auto autos1_ on garage0_.gid=autos1_.garageid 
           where garage0_.gid=?

**單條 SQL 完成主從關聯查詢**(LEFT OUTER JOIN),autos 即時填充。

核心差異總結

維度LAZY (預設)EAGER
SQL 數量1+N (N=關聯集合存取次數)1 (JOIN)
記憶體佔用低(按需載入)高(一次性載入全圖)
適用場景關聯不常用、大集合、效能敏感關聯極小、幾乎總是被使用
風險Session 關閉後存取拋出 LazyInitializationException過度載入導致記憶體/網路浪費

最佳實踐建議

  1. 預設堅持 LAZY,JPA 預設即是最佳實踐
  2. **明確需要時**才在 JPQL/EntityGraph 指定 JOIN FETCH 或 fetch=EAGER
  3. 避免在實體定義上硬編碼 EAGER——耦合查詢策略,失去彈性
  4. 大集合(如訂單明細、日誌)嚴禁 EAGER
  5. N+1 問題解決:@NamedEntityGraph + EntityManager.find(..., hints) 或 JPQL JOIN FETCH

補充:Hibernate 特有的 @LazyCollection(LazyCollectionOption.EXTRA)

極大集合時,size()/contains() 不載入全集合,改發 COUNT/EXISTS SQL,更精細控制。

抓取策略無銀彈,**按使用場景動態組合**才是正道。

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