傳統 Web 應用中,實體在視圖渲染階段若已分離(持久化上下文關閉),訪問延遲加載關係會拋出 LazyInitializationException。Open Session in View 模式雖能解決,但將持久化上下文綁定到請求作用域,導致跨請求的實體需重新附著/合併,增加複雜度。
Spring Web Flow 採用流作用域持久化上下文:在流啟動時創建 EntityManager/Session 並綁定到流作用域,整個流程複用同一上下文。實體在多請求間保持持久狀態,避免分離問題。變更作為髒實體緩存,直到流結束時才 flush() 並提交事務(原子流),或每步提交(非原子流)。
這與 Seam 對話(常用擴展型 EntityManager 注入狀態 Session Bean)有本質區別。
@Transactional 可調整流持久化特徵。默認語義:
@Transactional(readOnly=false, propagation=REQUIRED, isolation=ISOLATION_DEFAULT, timeout=TIMEOUT_DEFAULT)
流中所有數據訪問預設非事務性。Hibernate 設 auto-commit=true、FlushMode=MANUAL。實體由流作用域上下文管理,變更緩存為髒狀態。流結束時:
<end-state commit="true"/>:隱式 flush() 並提交<end-state commit="false"/>:捨棄變更此方式符合 JPA 1.0 對話處理規範,由 JpaFlowExecutionListener 實現。
全局套用 @Transactional(readOnly=true)(如 Hotel Booking 範例)。行為與非事務性類似,僅在流結束提交時刷新。優勢:防止服務層讀寫事務導致提前刷新。若服務層暴露為 Web Service 並標註 @Transactional,非事務性上下文會被覆蓋,觸發提前 flush;視圖層用 readOnly=true 可覆蓋服務層設定,保持只讀狀態。
Spring Web Flow 規定:事務初始化為只讀則始終只讀,反之亦然,忽略後續方法的 readOnly 屬性。
適用於長期運行流程(BPM)或無固定順序的頁面組群。事務作用域縮小到每個用戶操作(<transition>),而非整個流。
SWF 標籤不支援事務劃界,需在 @Transactional Bean 方法中發起,從 <evaluate> 調用。建議:
<transition> 下所有寫操作放入單一 @Transactional 方法,綁定同一事務readOnly=false(FlushMode=AUTO) / readOnly=true(FlushMode 切換)Hibernate 團隊建議統一用讀寫事務簡化編程。
非原子流強烈建議用樂觀鎖。實體更新後必須顯式查詢刷新 @Version 字段,否則後續事務更新同一實體會拋 OptimisticLockingFailureException(即使無併發)。這與原子流必須避免更新後查詢(會導致提前刷新)形成鮮明對比。
注入的上下文綁定到事務,跨請求實體分離。Hotel Booking 範例中 cancelBooking 需重新載入實體(em.find())才能刪除。視圖渲染階段延遲讀取會拋 LazyInitializationException。
非事務性訪問時,每次方法調用返回不同 EntityManager 實例,即時開關,實體無法跨調用保持持久狀態。
實體離開原流即分離,需合併/重新載入到目標流上下文。
<persistence-context/>)@Transactional(readOnly=true)@Transactional(readOnly=false),每操作一事務,更新後刷新版本