| 特性 | session.get() | session.load() |
|---|---|---|
| 回傳型別 | 實體類別實例 | 可回傳代理類別實例 (lazy=true 時) |
| 資料庫查詢時機 | 立即執行 SELECT | 延遲至首次存取非 ID 屬性 |
| 找不到資料 | 回傳 null | 拋出 ObjectNotFoundException (存取時) |
| 多型查詢支援 | 完整支援 | 僅 lazy=false 時支援 |
| Session 關閉後存取 | 安全 (資料已載入) | 若未初始化會拋 LazyInitializationException |
當實體映射檔 <class lazy="true"> (預設) 時:
load(User.class, 1L) 產生 動態代理物件 (CGLIB/ByteBuddy),僅設定 ID,其他屬性為 null。Map<Serializable, Object> (key=ID)。user.getName() 等 getter 時,攔截器觸發 初始化:檢查一級/二級快取 → 發送 SELECT → 填充代理物件屬性。user.getId() 不觸發初始化,直接從快取 key 取值。// 繼承結構: 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,導致多型邏輯失效。
// 錯誤示範
@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() 觸發初始化
// load() 刪除不存在 ID → 異常
session.delete(session.load(User.class, 999)); // 拋 ObjectNotFoundException
// get() 安全
User u = session.get(User.class, 999);
if (u != null) session.delete(u);
代碼生成器預設用 get() 確保回傳實體已載入,避免上層 Service 在 Session 關閉後存取屬性報錯。若確定 ID 必存在且需效能,可手動改為 load()。
load() 享受延遲載入效能。get() 安全判斷 null。get() 或 load(entity, id, LockMode.NONE) (Hibernate 5.2+)。get() 或配合 @Transactional 管理 Session 生命週期。理解代理、快取、異常拋出時機,才能在正確性與效能間取得平衡。