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

Hibernate 舊記錄時間戳意外更新:Spring 事務與實體狀態管理深度解析

     編輯:程式設計綜合問答

問題現象

使用 Hibernate 實作「軟刪除 + 新增新版本」邏輯:將舊照片記錄 isAvailable 從 'Y' 改為 'N',預期 initTime 不變,但實際執行後舊記錄的 initTime 被更新為比新記錄晚約 20 秒。

根因分析

核心問題在於 Hibernate Session 的實體狀態管理 與 Spring 事務邊界 的交互:

  1. getImageInfoByUserIdAndType 查詢返回的 TImageInfo 實體處於 Persistent(持久)狀態,受 Session 管理。
  2. 迴圈中修改 setIsNew(IS_NEW_VALUE_NO) 並呼叫 saveOrUpdate,Hibernate 會將此實體標記為 dirty。
  3. 關鍵點:若實體映射檔或註解中 initTime 定義為 @Version、@Generated(INSERT) 或資料庫欄位有 ON UPDATE CURRENT_TIMESTAMP,Hibernate 在 flush 時可能重新產生或資料庫自動更新該欄位。
  4. 同一個事務中,先更新舊記錄,再 save(newEntity),Session flush 時會一次性執行所有 SQL,順序不保證,導致時間差異。

實體映射檢查重點

// 檢查註解
@Column(name = "initTime", updatable = false)  // 關鍵:禁止 Hibernate 更新
@CreationTimestamp  // 僅插入時自動賦值
private Timestamp initTime;

// 或 XML 映射

若欄位定義允許更新(update="true" 或無 updatable=false),Hibernate 會在 dirty check 時包含該欄位。

資料庫層面檢查

-- MySQL 範例:檢查欄位定義
SHOW CREATE TABLE t_image_info;
-- 若看到:initTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
-- 則資料庫層面會自動更新,必須移除 ON UPDATE 子句
ALTER TABLE t_image_info MODIFY initTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP;

程式碼修正方案

方案一:使用合併而非更新(推薦)

// 將舊記錄設為不可用,但不觸發完整 dirty check
for (BaseModel item : imagesResult) {
    TImageInfo old = (TImageInfo) item;
    old.setIsNew(IS_NEW_VALUE_NO);
    // 使用 HQL 直接更新單一欄位,避免載入完整實體
    getSession().createQuery(
        "UPDATE TImageInfo SET isNew = :val WHERE id = :id")
        .setParameter("val", IS_NEW_VALUE_NO)
        .setParameter("id", old.getId())
        .executeUpdate();
}

方案二:分離事務(若業務允許)

將「舊記錄軟刪除」與「新增新記錄」拆為兩個獨立 @Transactional 方法,確保 Session 隔離。

方案三:手動清除 dirty 狀態(不推薦,治標不治本)

// 修改前先 evict,修改後 merge(複雜且易錯)
session.evict(old);
old.setIsNew(IS_NEW_VALUE_NO);
session.merge(old);

Spring 事務配置檢查

原配置使用 BeanNameAutoProxyCreator 攔截 *Service,屬舊式 AOP。建議改用 @EnableTransactionManagement + @Transactional 註解,更清晰控制事務邊界與傳播行為。

結語

時間戳意外更新通常源於「實體映射允許更新」或「資料庫欄位自動更新」。加上 updatable=false / update="false" 並檢查資料庫 DDL,配合 HQL 單欄位更新避免完整實體 dirty check,即可徹底解決。Spring 事務配置現代化亦能減少此類隱性問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved