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

Hibernate load() vs get() 深度解析:代理、延遲載入、多型查詢與實戰陷阱

     編輯:Java 相關知識

核心差異速查表

特性session.get()session.load()
回傳型別實體類別實例可回傳代理類別實例 (lazy=true 時)
資料庫查詢時機立即執行 SELECT延遲至首次存取非 ID 屬性
找不到資料回傳 null拋出 ObjectNotFoundException (存取時)
多型查詢支援完整支援僅 lazy=false 時支援
Session 關閉後存取安全 (資料已載入)若未初始化會拋 LazyInitializationException

load() 延遲載入機制詳解

當實體映射檔 <class lazy="true"> (預設) 時:

  1. load(User.class, 1L) 產生 動態代理物件 (CGLIB/ByteBuddy),僅設定 ID,其他屬性為 null。
  2. 不發送 SQL,一級快取放入 Map<Serializable, Object> (key=ID)。
  3. 呼叫 user.getName() 等 getter 時,攔截器觸發 初始化:檢查一級/二級快取 → 發送 SELECT → 填充代理物件屬性。
  4. user.getId() 不觸發初始化,直接從快取 key 取值。

get() 即時載入邏輯

  1. 檢查一級快取 (Session) → 二級快取 (SessionFactory) → 資料庫。
  2. 若一級快取已存在同 ID 的代理物件 (先前 load 過),直接回傳該代理,不轉為實體類別。後續存取屬性時才初始化。
  3. 資料庫無對應列 → 回傳 null。

多型查詢差異實例

// 繼承結構: Animal <- Cat, Dog
// 映射: 
//         
//         
//       

Animal a1 = session.get(Animal.class, 1); // 正確回傳 Cat 或 Dog 實例
Animal a2 = session.load(Animal.class, 1); // lazy=true 時回傳 Animal$Proxy,無法判斷實際子類別

load() 代理物件繼承自根類別 Animal,instanceof Cat 為 false,導致多型邏輯失效。

實戰常見陷阱與對策

1. Session 關閉後存取代理屬性 → LazyInitializationException

// 錯誤示範
@Transactional(readOnly = true)
public UserDTO findDto(Long id) {
    User u = session.load(User.class, id); // 回傳代理
    return new UserDTO(u.getName()); // 拋出異常,Session 已關閉
}

// 正確做法 A: 用 get()
User u = session.get(User.class, id);

// 正確做法 B: 在 Transaction 內強制初始化
User u = session.load(User.class, id);
Hibernate.initialize(u); // 或 u.getName() 觸發初始化

2. 刪除不存在的實體

// load() 刪除不存在 ID → 異常
session.delete(session.load(User.class, 999)); // 拋 ObjectNotFoundException

// get() 安全
User u = session.get(User.class, 999);
if (u != null) session.delete(u);

3. MyEclipse 產生 DAO 用 get() 的原因

代碼生成器預設用 get() 確保回傳實體已載入,避免上層 Service 在 Session 關閉後存取屬性報錯。若確定 ID 必存在且需效能,可手動改為 load()。

最佳實務建議

  • ID 必定存在 (如外鍵關聯、內部邏輯) → load() 享受延遲載入效能。
  • ID 可能不存在 (使用者輸入、外部參數) → get() 安全判斷 null。
  • 需多型判斷 → get() 或 load(entity, id, LockMode.NONE) (Hibernate 5.2+)。
  • Session 邊界不明確 → 偏好 get() 或配合 @Transactional 管理 Session 生命週期。

理解代理、快取、異常拋出時機,才能在正確性與效能間取得平衡。

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