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

Hibernate 延遲加載與緩存機制深度解析

     編輯:Java 綜合教學

Hibernate 延遲加載與緩存機制深度解析

什麼是延遲加載?

延遲加載是指:當應用程式需要從資料庫獲取物件時(未設置 lazy="false"),Hibernate 僅從資料庫獲取符合條件物件的 OID(對象標識符)並生成代理物件,並不會立即加載物件的所有屬性值。只有當程式真正訪問該物件的屬性時,才會觸發資料庫查詢加載相應數據。簡單說,就是「按需加載」,盡可能減少不必要的數據查詢量。

如何配置延遲加載

在 Hibernate 中通過 .hbm 映射文件中的 lazy 屬性配置。lazy 屬性出現的位置不同,其作用和取值也不同:

1. Class 標籤中的 lazy(類級別)

  • true(預設):啟用延遲加載
  • false:立即加載(不包含自定義類型屬性)

2. 多對一關聯中的 lazy

  • proxy(預設):延遲加載(使用代理)
  • no-proxy:無代理延遲加載(需字節碼增強)
  • false:立即加載

3. Set/集合元素中的 lazy

  • true(預設):延遲加載
  • false:立即加載
  • extra:加強延遲加載(極智能模式,size()、contains() 不觸發全量加載)

實戰驗證:get() vs load() 的關鍵差異

測試發現:使用 session.get() 獲取物件時,會忽略類級別的 lazy 設置,強制立即加載。只有使用 session.load() 才能真正觸發延遲加載行為。這是開發中極易踩坑的點。

延遲加載引發的「no Session」問題與解決

問題場景:分層架構中,DAO/Biz 層操作數據,但 Session 在 Service 層關閉。JSP 頁面渲染時訪問未加載的關聯屬性,Hibernate 嘗試查詢資料庫卻發現無可用 Session,拋出 LazyInitializationException。

經典解決方案:Open Session in View 模式

// Filter 實現
public class HibernateFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        Session session = HibernateUtil.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            chain.doFilter(request, response);
            tx.commit();
        } catch (Exception e) {
            if (tx != null) tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }
}

在 web.xml 配置該 Filter,確保 Session 生命週期覆蓋整個 HTTP 請求,包括視圖渲染階段。


Hibernate 緩存體系詳解

緩存分類

  • 一級緩存(內部緩存/事務級緩存):Session 級別,生命週期與 Session 綁定,不可關閉
  • 二級緩存(SessionFactory 級別):進程/集群範圍共享,可配置插件(如 EhCache)
  • 查詢緩存:基於 HQL/SQL 查詢結果的緩存,依賴二級緩存

一級緩存機制演示

// 同一 Session 內兩次查詢同一 OID
Employee emp1 = session.get(Employee.class, 1L);
Employee emp2 = session.get(Employee.class, 1L);
// 只執行 1 條 SQL,emp1 == emp2 為 true(同一記憶體引用)

一級緩存存儲的是物件的記憶體引用,而非屬性值副本。

二級緩存配置實戰(以 EhCache 為例)

  1. 引入依賴:ehcache.jar、backport-util-concurrent.jar、commons-logging.jar
  2. hibernate.cfg.xml 啟用:
    <property name="hibernate.cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
  3. 配置實體類進入二級緩存:
    <class-cache usage="read-write" class="cn.happy.entity.Emp"/>
  4. 提供 ehcache.xml 配置文件

關鍵差異:二級緩存存儲的是「散裝屬性」(屬性值組合),而非物件引用。每次從二級緩存獲取都會重新組裝物件,因此兩次查詢得到的物件記憶體地址不同。

集合緩存與查詢緩存

若需緩存集合(如 Dept 下的 Employee 集合),需在 hibernate.cfg.xml 添加:

<collection-cache collection="cn.happy.entity.Dept.emps" usage="read-write"/>

查詢緩存需額外啟用:

<property name="hibernate.cache.use_query_cache">true</property>
// 使用時
query.setCacheable(true);

查詢緩存的 Key 是 Hibernate 內部生成的 SQL 語句,而非 OID。


進階議題:延遲加載與緩存的交互細節

lazy 與 fetch 屬性的聯合作用

  • lazy:決定關聯物件初始化時機
  • fetch:決定 SQL構建形式(join/select/subselect)

當 fetch="join" 時,強制立即加載,忽略 lazy 設置,採用左外連接一次性查詢。

Query.list() vs Query.iterate()

特性list()iterate()
返回類型ListIterator
查詢方式直接查資料庫先查 ID,再按需查詢物件(N+1 問題風險)
緩存利用不主動使用二級緩存(除非配合查詢緩存)優先檢查二級緩存
返回物件真實物件代理物件

緩存內部存儲結構總結

  • 均以 Map 結構存儲
  • Key:一級/二級緩存用 OID,查詢緩存用 SQL
  • Value:一級/查詢緩存存物件引用,二級緩存存散裝屬性
  • 讀取順序:一級/查詢緩存 → 二級緩存 → 資料庫

二級緩存持久化到磁盤

在 ehcache.xml 中配置 <diskStore path="..."/> 並將 maxElementsInMemory 設置為 0 即可實現緩存落盤。

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