一個 **Filter/攔截器** 模式:在請求進入控制器前開啟 Hibernate Session 並綁定到當前執行緒,請求結束(視圖渲染完成)後再關閉 Session。解決**「視圖層存取關聯實體觸發 LazyInitializationException」**問題。
請求進入
↓
OSIV Filter: open Session → bind ThreadLocal
↓
Controller → Service → Repository (正常使用 Session)
↓
View 渲染 (JSP/Thymeleaf 遍歷 @OneToMany/@ManyToMany 關聯)
↓
OSIV Filter: unbind → close Session / flush
↓
響應返回

<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value> <!-- true: 單 Session 模式; false: DeferredClose 模式 -->
</init-param>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
| 問題 | 影響 | 嚴重度 |
|---|---|---|
| **Session 持有時間過長** | 資料庫連接佔用整個請求週期,高併發下連接池耗盡 | ⭐⭐⭐⭐⭐ |
| **N+1 查詢失控** | 視圖層隱性觸發大量關聯查詢,無法在 Service 層統一優化 (fetch join / batch size) | ⭐⭐⭐⭐ |
| **事務邊界模糊** | 視圖渲染期間意外觸發寫操作導致髒數據提交,或唯讀事務被污染 | ⭐⭐⭐ |
| **測試困難** | 單元測試需模擬 Filter 鏈,整合測試啟動慢 | ⭐⭐ |
| **非 HTTP 請求場景失效** | 定時任務、消息消費、CLI 命令無 Filter,需手工管理 Session | ⭐⭐ |
| 方案 | 核心思想 | 優點 | 缺點 | 推薦場景 |
|---|---|---|---|---|
| **DTO 投影 / View Model** | Service 層查詢精確欄位,組裝 DTO 返回視圖 | 零 Lazy 問題、SQL 可控、介面契約清晰、易測試 | 需編寫映射代碼 (MapStruct 自動化) | **所有新專案首選** |
| **@EntityGraph / Fetch Join** | Repository 方法聲明式指定預載關聯 | 保留實體模型、單次查詢拿齊數據 | 圖定義分散、複雜圖難維護 | 簡單關聯、少量實體 |
| **EXTENDED PersistenceContext (CDI/Spring)** | Session 作用域延伸至會話,手動 flush/clear | 長會話編輯場景自然契合 | 記憶體洩漏風險、併發衝突處理複雜 | 向導式多步表單、編輯器 |
| **OSIV (唯讀模式)** | singleSession=false + flushMode=MANUAL | 保留視圖讀取便利、禁止寫入 | 仍佔用連接、需嚴格代碼審查 | 存量系統過渡期 |
@Transactional(readOnly=true) + EntityGraph 過渡。OSIV 是**「為了解決視圖層 Lazy 問題而犧牲架構純粹性」的妥協產物**。現代開發應採用 **DTO 投影** 為主、EntityGraph 為輔的組合,徹底在 Service 層封裝資料存取邏輯,視圖層純粹渲染。**連接池是寶貴資源,不應為了方便而浪費在視圖渲染等待上。**