首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

jQuery AJAX 回調不執行且無錯誤排查:Spring 事務結束導致 Lazy Loading 失敗與 JSON 序列化循環引用

     編輯:更多程式設計知識

現象:`$.post` 請求 Struts2 Action,伺服器邏輯正常執行、資料庫查詢無誤,**但瀏覽器 Network 面板無回應、success/error 回調皆不觸發、無 JS 錯誤**。根因:**jQuery 解析回應 JSON 時觸發序列化錯誤,導致整個 AJAX 處理管線中斷且靜默失敗**。

核心原因鏈

  1. Action 返回 `Collection`,`Privilege` 含 `Set`,`User` 又含 `Set` → **雙向關聯循環引用**
  2. Spring 事務在 Action 返回前**已提交/關閉** (Open Session In View 未生效或配置錯誤)
  3. JSON 序列化器 (Jackson/Gson) 遍歷物件圖時,嘗試存取 `Privilege.users` → 觸發 Hibernate Lazy Loading → **無 Session 異常**
  4. 異常發生在 **序列化階段** (View 層),非 Action 執行階段,Struts2 不會進入 `exception` 拦截器
  5. jQuery 收到**斷裂/非 JSON 回應** (或空回應),解析拋錯,**但錯誤被 jQuery 內部捕獲並靜默**,`error` 回調不觸發 (因 HTTP 狀態碼 200 OK)

三種解決方案 (按推薦順序)

方案一:JSON 序列化忽略循環引用屬性 (最快、無侵入)

// Privilege 實體類
@JsonIgnoreProperties({"users"})  // Jackson
// 或
@JsonIgnore
public Set getUsers() { return users; }

// Gson 則需
@Expose(serialize = false) transient Set users;

序列化時直接跳過 `users` 集合,避免觸發 Lazy Loading 與循環。

方案二:Service 層預先初始化關聯集合 (根治 Lazy Loading)

@Transactional(readOnly = true)
public List findAllWithUsers() {
  List list = privilegeDao.findAll();
  list.forEach(p -> Hibernate.initialize(p.getUsers())); // 強制載入
  return list;
}

在事務邊界內完成初始化,View 層序列化時無需再查庫。

方案三:啟用 Open Session In View (OSIV) / Open EntityManager In View



  openSessionInViewFilter
  org.springframework.orm.hibernate5.support.OpenSessionInViewFilter


  openSessionInViewFilter
  /*

將 Session 生命週期延伸至 View 渲染結束,**但可能導致 N+1 查詢、連線洩漏、事務邊界模糊**,生產環境謹慎使用。

診斷技巧:如何確認是序列化問題

  1. 瀏覽器 Network → 查看該請求 **Response** 標籤:若顯示「failed」或截斷 JSON,即為序列化中斷
  2. 伺服器日誌搜尋 `JsonMappingException`、`LazyInitializationException`、`could not initialize proxy`
  3. 臨時在 Action 中 `return null` 或返回簡單字串,若 AJAX 回調正常執行 → 確認是回應資料問題

最佳實踐總結

  • **DTO 模式**:Action 返回專用 DTO (無循環、無 Lazy、扁平結構),而非領域實體
  • **@JsonView / @JsonIgnore** 精細控制序列化輸出
  • **全局異常處理**:配置 `HandlerExceptionResolver` 捕獲序列化異常並返回標準錯誤 JSON,確保 `error` 回調可觸發
  • **關閉 OSIV**,明確在 Service 層 `@Transactional` 內完成資料裝配
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved