延遲加載是指:當應用程式需要從資料庫獲取物件時(未設置 lazy="false"),Hibernate 僅從資料庫獲取符合條件物件的 OID(對象標識符)並生成代理物件,並不會立即加載物件的所有屬性值。只有當程式真正訪問該物件的屬性時,才會觸發資料庫查詢加載相應數據。簡單說,就是「按需加載」,盡可能減少不必要的數據查詢量。
在 Hibernate 中通過 .hbm 映射文件中的 lazy 屬性配置。lazy 屬性出現的位置不同,其作用和取值也不同:
true(預設):啟用延遲加載false:立即加載(不包含自定義類型屬性)proxy(預設):延遲加載(使用代理)no-proxy:無代理延遲加載(需字節碼增強)false:立即加載true(預設):延遲加載false:立即加載extra:加強延遲加載(極智能模式,size()、contains() 不觸發全量加載)測試發現:使用 session.get() 獲取物件時,會忽略類級別的 lazy 設置,強制立即加載。只有使用 session.load() 才能真正觸發延遲加載行為。這是開發中極易踩坑的點。
問題場景:分層架構中,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 請求,包括視圖渲染階段。
// 同一 Session 內兩次查詢同一 OID
Employee emp1 = session.get(Employee.class, 1L);
Employee emp2 = session.get(Employee.class, 1L);
// 只執行 1 條 SQL,emp1 == emp2 為 true(同一記憶體引用)
一級緩存存儲的是物件的記憶體引用,而非屬性值副本。
ehcache.jar、backport-util-concurrent.jar、commons-logging.jarhibernate.cfg.xml 啟用:<property name="hibernate.cache.use_second_level_cache">true</property><property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property><class-cache usage="read-write" class="cn.happy.entity.Emp"/>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:決定 SQL構建形式(join/select/subselect)當 fetch="join" 時,強制立即加載,忽略 lazy 設置,採用左外連接一次性查詢。
| 特性 | list() | iterate() |
|---|---|---|
| 返回類型 | List | Iterator |
| 查詢方式 | 直接查資料庫 | 先查 ID,再按需查詢物件(N+1 問題風險) |
| 緩存利用 | 不主動使用二級緩存(除非配合查詢緩存) | 優先檢查二級緩存 |
| 返回物件 | 真實物件 | 代理物件 |
在 ehcache.xml 中配置 <diskStore path="..."/> 並將 maxElementsInMemory 設置為 0 即可實現緩存落盤。