Hibernate 作為經典 ORM 框架,性能核心在於**緩存體系**。理解對象三種狀態、兩級緩存交互、CRUD 觸發的緩存行為,是避開性能陷阱、寫出高效持久層代碼的前提。
| 狀態 | 別名 | 定義 | 關鍵特徵 |
|---|---|---|---|
| Transient (瞬時) | VO / DTO | new 出來,無 ID,未關聯 Session | 不在緩存,無數據庫記錄對應 |
| Persistent (持久) | PO | 關聯 Session,有 ID,處於 Session 生命週期內 | **一級緩存**、自動髒檢查、延遲加載、級聯操作 |
| Detached (游離) | VO / DTO | Session 關閉/清理/evict 後的持久對象 | 有 ID,無 Session,可序列化傳輸,需 reattach 才能持久化 |
狀態流轉:
new Entity() → Transient
save()/persist() → Persistent
Session.close()/evict() → Detached
update()/merge()/lock() → Persistent (再次關聯)
delete()/remove() → Removed (提交後 Transient)
Session (通常 = 一個請求/事務)。save()、get()、load()、update()、merge()、lock() 都會**放入/查找**一級緩存。flush() 同步到數據庫,不清空緩存。clear() / evict(entity) 手動清理。hibernate.cache.use_second_level_cache=true + 提供商 (Ehcache/Infinispan/Redis/Hazelcast)。read-only:只讀參考數據,最高性能。nonstrict-read-write:極少寫入,允許臨時不一致。read-write:讀寫並發,使用軟鎖。transactional:XA 事務緩存 (極少用)。| 操作 | 一級緩存 | 二級緩存 | SQL 發送時機 | 注意事項 |
|---|---|---|---|---|
save() | 放入 | **不放入** (默認) | flush/commit | ID 生成策略影響 (identity 需立即 INSERT) |
saveOrUpdate() | 查找→決策 save/update | 不直接操作 | flush/commit | 批量操作必須定期 flush()+clear() 防 OOM |
delete() | 標記移除 | 同步失效 (若啟用) | flush/commit | **批量刪除會先載入所有對象到內存** → OOM 風險,建議 HQL 批量刪除或分頁迭代 |
update() | 重新關聯 (游離→持久) | 同步失效 | flush (不立即) | 持久態對象調用 update 無效;髒檢查自動更新更優雅 |
merge() | 複製狀態到持久實例 | 同步失效 | flush | 返回**新持久實例**,原游離對象不變 |
get() | 查找→無則查 DB→放入 | 查找→無則查 DB→放入 | 立即 (若無緩存) | 返回 null 表示不存在 |
load() | 查找→無則建代理→放入 | 查找→無則建代理→放入 | **首次訪問屬性時** (延遲) | 不存在拋異常;適合「確信存在」場景 |
createQuery().list() | **不讀緩存,只寫入** (實體放入一級) | **不讀寫** (除非開 Query Cache) | 立即 | 大結果集慎用,內存暴增 |
createQuery().iterate() | 查 ID 列表 (先查一級/二級) → 再按 ID 逐條 load | 查找 ID/實體 | N+1 查詢 (極慢) | **僅當實體已在緩存** 才有優勢,否則慎用 |
List orders = session.createQuery("FROM Order").list(); // 1 查詢
for (Order o : orders) {
System.out.println(o.getCustomer().getName()); // N 查詢 (每個 Order 觸發 1 次)
}
@ManyToOne(fetch = FetchType.EAGER) 或 HQL FROM Order o JOIN FETCH o.customer。@BatchSize(size=20),Hibernate 會用 IN (id1, id2...) 批量載入關聯。@Fetch(FetchMode.SUBSELECT),二次查詢載入所有關聯。hibernate.cache.use_query_cache=true + 二級緩存提供商。query.setCacheable(true)。// 批量插入/更新
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for (int i = 0; i < 10000; i++) {
Entity e = new Entity(...);
session.save(e);
if (i % 50 == 0) { // 與 JDBC 批處理 size 一致
session.flush();
session.clear(); // 關鍵:清一級緩存防 OOM
}
}
tx.commit();
session.close();
// 批量刪除 (HQL 直通數據庫,不載入對象)
session.createQuery("DELETE FROM Entity WHERE status = :st")
.setParameter("st", "OLD")
.executeUpdate();
get/load 極快。