概述#
Persistence Context 作為 transactional write-behind cache,Hibernate Session 同時扮演 first-level cache 和 entity state transition buffer 的角色。EntityManager 和 Session 提供多種 entity state management 方法:
persist:將 transient entity 轉為 managed 狀態merge:將 detached entity 的內部狀態複製到新載入的 entity instanceupdate/saveOrUpdate/lock:Hibernate 特有的 reattaching 方法,不需要重新載入 entityremove/delete:將 entity 標記為移除
Persistence Context 透過 flushing 將記憶體中的 persistent state 同步到底層資料庫。在 flush 時,Hibernate 可偵測 managed entity 是否已被修改,並觸發對應的 table row update,這個過程稱為 dirty checking。
使用 write-behind cache 的優勢:
- Entity state changes 被 buffer 後可延遲執行,最小化 row-level lock 的持有時間
- 一次性執行所有 statement,可利用 JDBC batch updates 避免逐條 roundtrip
中間層的 write-behind cache 也帶來挑戰:Persistence Context 可能產生 data inconsistency。理解 flushing 的內部運作機制,才能在不影響 data consistency 的前提下進行最佳化。
Flush Modes#
Persistence Context 可透過手動或自動方式 flush。EntityManager 和 Session 都提供 flush() 方法,用於手動觸發同步。
手動 flush 的時機:
- Query 執行前:確保 in-memory 的變更對當前 query 可見,防止 read-your-own-writes 一致性問題
- Transaction commit 前:確保 in-memory 的變更被持久化,避免 Persistence Context 關閉後遺失
JPA Flush Mode Types#
JPA 定義兩種自動 flush mode:
FlushModeType.AUTO(預設):在每個 query(JPQL 或 native SQL)執行前及 transaction commit 前觸發 flushFlushModeType.COMMIT:僅在 transaction commit 前觸發 flush
Hibernate Flush Modes#
Hibernate 定義四種 flush mode:
FlushMode.AUTO(預設):在 transaction commit 時 flush;但不一定在每個 query 前 flush,採用 smart flushing 機制FlushMode.ALWAYS:在每個 query(HQL 或 native SQL)和 transaction commit 前都 flushFlushMode.COMMIT:僅在 transaction commit 時 flushFlushMode.MANUAL:停用自動 flush,只能手動呼叫flush()
JPA 的
FlushModeType.AUTO較接近 Hibernate 的FlushMode.ALWAYS,而非FlushMode.AUTO。Hibernate 的FlushMode.AUTO不會在每個 query 前觸發 flush。
FlushMode.AUTO 的 SQL Query 一致性#
Hibernate 的 FlushMode.AUTO 採用 smart flushing 機制:執行 HQL query 時,Hibernate 會檢查當前 query 涉及的 table space,僅在有 pending entity state transition 匹配該 table space 時才觸發 flush。此最佳化可減少不必要的 flush 呼叫。
但對於 native SQL query,Hibernate 無法解析其涉及的資料庫 table,因此會在每個 native SQL query 前一律 flush。若要避免此行為,開發者可以:
- 將 query 的 flush mode 設為
FlushMode.ALWAYS以確保一致性 - 使用
addSynchronizedEntityClass()明確告知 Hibernate 該 native query 涉及哪些 table space
僅 Hibernate native API(
Session)使用 smart flushing。使用 JPAEntityManager時,Hibernate 會在每個 JPQL 或 native SQL query 前 flush。
Events and the Action Queue#
每個 entity state change 在 Hibernate 內部會產生對應的 event(如 PersistEvent、MergeEvent、DeleteEvent),並由對應的 event listener 處理(如 DefaultPersistEventListener、DefaultMergeEventListener、DefaultDeleteEventListener)。
Event listener 將 entity state transition 轉換為內部的 EntityAction,在 flush time 才實際執行:
EntityInsertAction/EntityIdentityInsertAction:transient entity 變為 persistent 時產生,觸發 SQL INSERT。使用 identity generator 時,INSERT 必須立即執行以取得 identifierEntityUpdateAction:managed entity 被修改時產生,觸發 SQL UPDATEEntityDeleteAction:entity 被標記為 removed 時產生,觸發 SQL DELETEOrphanRemovalAction:帶有 orphan removal 的 association 在 dereferencing child entity 時產生
所有 pending entity actions 儲存在 ActionQueue 中,在 flush time 統一執行。
Entity state transitions 可透過 cascade 從 parent entity 傳播到 child entity。例如 cascading persist 時,Hibernate 的行為等同於對每個 child entity 手動呼叫
persist。
Flush Operation Order#
Flush 時,Hibernate 以嚴格的固定順序執行所有 EntityAction:
OrphanRemovalActionEntityInsertAction和EntityIdentityInsertActionEntityUpdateActionCollectionRemoveActionCollectionUpdateActionCollectionRecreateActionEntityDeleteAction
這個固定的執行順序可能導致非預期的 constraint violation。例如先
remove舊 entity 再persist同 unique key 的新 entity 時,由於 INSERT 排在 DELETE 之前執行,會觸發 unique constraint violation。解決方式是在remove之後手動呼叫flush(),確保 DELETE 先被執行。
實務上更好的做法是直接 UPDATE 既有 entity 而非先刪除再新增,因為 UPDATE 比 INSERT + DELETE 組合更有效率。
Dirty Checking#
Entity 從 transient 變為 managed 時,Hibernate 發出 SQL INSERT;entity 被標記為 removed 時,發出 SQL DELETE。UPDATE 則不同——它沒有對應的顯式 entity state transition,而是由 Persistence Context 追蹤 entity 的內部狀態,在 flush time 將修改過的 entity 轉換為 UPDATE statement。
The Default Dirty Checking Mechanism#
預設情況下,Hibernate 在 entity 載入時會在 Persistence Context 中保存一份 loading-time snapshot(以 Object[] 陣列表示,接近底層 table row 的值)。在 flush time,每個 entity attribute 都會與其 loading-time value 比較。
Dirty check 的次數由以下公式決定:
N = n * p(n = managed entities 數量,p = entity attributes 數量)
即使只有一個 attribute 被修改,Hibernate 仍需遍歷所有 managed entities。當 managed entities 數量龐大時,default dirty checking 會對 CPU 資源造成顯著影響。

Figure 12.1: Default dirty checking mechanism
Controlling the Persistence Context Size#
由於 loading-time snapshot 獨立保存,Persistence Context 儲存一個 managed entity 需要 兩倍記憶體。控制 Persistence Context 大小的策略:
Read-only mode:不需修改的 entity 應以 read-only 方式載入,可在
Session層級或 query 層級設定:- Session 層級:
session.setDefaultReadOnly(true) - Query 層級:
.setHint(QueryHints.HINT_READONLY, true)
- Session 層級:
Read-only entity 不保存 loading-time snapshot,也不參與 dirty checking,同時節省 記憶體和 CPU
Batch processing:大量 entity 操作時,應定期
flush()和clear()Persistence Context,並將 Persistence Context 跨多個 database transaction 分割,每個 batch iteration 清空後開始新的 transaction
Persistence Context 應盡量保持小規模。只有需要修改的 entity 才應成為 managed 狀態。Read-only transaction 應使用 DTO projections 或以 read-only mode 載入 entity。
Bytecode Enhancement#
Hibernate 長期支援 bytecode enhancement,但在 Hibernate 5 之前 dirty checking 並未使用此功能。Hibernate 5 重新實作了 bytecode instrumentation 機制,可避免基於 reflection 的 dirty checking。
Bytecode enhancement 可在 compile-time、runtime 或 deployment 時進行,建議使用 compile-time 方式:
- Enhanced classes 可被 unit test 涵蓋
- 應用程式啟動更快(不需 runtime instrumentation)
- 避免 class loading 問題
Bytecode enhancement plugin 支援三個 instrumentation 選項:
- Lazy initialization:允許 entity attributes 延遲載入
- Dirty tracking:entity 自行追蹤 attribute 變更
- Association management:自動同步 bidirectional association 的兩端
啟用 dirty tracking 後,Hibernate 會在 entity class 中注入 $$_hibernate_tracker 屬性。每個 setter method 被修改為同時呼叫 $$_hibernate_trackChange 方法記錄變更。在 flush time,Hibernate 透過 $$_hibernate_hasDirtyAttributes 和 $$_hibernate_getDirtyAttributes 方法快速判斷 entity 是否被修改,無需逐一比較所有屬性。
效能測試顯示,在 50 個以下的 entity 數量時,reflection-based 和 bytecode enhancement 的 dirty checking 效能相當。當 entity 數量達到 100 個以上時,bytecode enhancement 才開始展現優勢。由於單次 dirty checking 的時間相較於 database query 仍然極短,in-memory attribute tracking 的實際效能提升在多數場景中並不顯著。
