在 Hibernate 資料存取操作中,Session.load() 與 Session.get() 是最常用的兩種載入實體方法。雖然都能根據識別值取得物件,但它們在延遲載入支援、無資料時行為、SQL 發送時機上有本質差異。選錯方法可能導致效能問題或執行期例外。
| 特性 | get() | load() |
|---|---|---|
| 延遲載入 | 不支援 | 支援 (回傳 Proxy) |
| 查無資料 | 回傳 null | 拋出 ObjectNotFoundException |
| SQL 發送時機 | 立即執行 | 首次存取屬性時 |
| 回傳型別 | 實體實例 | Proxy 代理物件 |
public void testGetMethod() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
// 立即發送 SELECT SQL
User user = (User) session.get(User.class, "402880d01b9bf210011b9bf2c2ff0002");
System.out.println("user.name=" + user.getName()); // 即使不呼叫這行,SQL 已執行
user.setName("張三");
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
}
行為特徵: 第 8 行執行時,無論後續是否使用物件,Hibernate 都會立即查詢資料庫。查無資料回傳 null,需自行判斷避免 NPE。
public void testLoadMethod() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
// 不發送 SQL,回傳 Proxy 代理物件
User user = (User) session.load(User.class, "402880d01b9bf210011b9bf2b2ff0002");
// 這行觸發真實 SQL 查詢
System.out.println("user.name=" + user.getName());
user.setName("李四");
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
}
行為特徵: 第 10 行僅建立 Proxy 物件,不查資料庫。第 11 行首次存取屬性時才發送 SQL。若 ID 不存在,存取屬性時拋出 ObjectNotFoundException。
load() 利用 CGLIB/ByteBuddy 動態代理 建立實體子類別實例:
order.setCustomer(session.load(Customer.class, id)))在 IDE 設定斷點觀察變數變化:
get():執行行後,變數立即顯示實體欄位值load():執行行後,變數顯示為 User_$$_javassist_123 (Proxy 類別),展開可見 handler 欄位持有識別值掌握兩者差異,能讓資料存取層更精準、更高效。