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

Hibernate 自增主鍵更新操作導致數據重複的原因與解決

     編輯:程式設計疑難解答

問題現象

數據庫主鍵配置為自增 (MySQL AUTO_INCREMENT, Oracle Sequence, SQL Server IDENTITY)。原有 1 條數據,執行 Hibernate update() 後,數據庫變成 2 條,主鍵分別為原 ID 和新自增 ID。

根因分析:saveOrUpdate 語義

Hibernate Session.update() 僅用於持久化對象 (已有 ID,且數據庫存在該記錄)。若對象處於遊離狀態 且 ID 為空/默認值 (0/null),Hibernate 會誤以為是瞬時對象,執行 save() 邏輯插入新記錄。

常見觸發場景:

  1. ID 未正確設置:前端傳遞的對象 ID 為 0 或 null,後端直接 update(entity)。
  2. 使用了 assigned 生成器:映射文件 `` 但代碼邏輯未手動賦值 ID。
  3. 版本號/時間戳衝突:樂觀鎖字段導致 Hibernate 認為對象已過期,嘗試重新保存。

映射配置對比

<!-- 數據庫自增:推薦 identity (MySQL/SQL Server) 或 sequence (Oracle/PostgreSQL) -->
<id name="id" column="id">
    <generator class="identity"/> <!-- 或 native, sequence -->
</id>

<!-- 錯誤示例:assigned 需手動賦值 ID -->
<id name="id" column="id">
    <generator class="assigned"/>
</id>

解決方案

方案一:確保更新前對象攜帶正確 ID (最根本)

// Controller 入參接收 ID
@PostMapping("/update")
public String update(@ModelAttribute("entity") Entity entity) {
    // 必須從數據庫加載原對象,再複製屬性,保證 ID 不丟失
    Entity persisted = service.findById(entity.getId());
    if (persisted != null) {
        BeanUtils.copyProperties(entity, persisted, "id", "createTime"); // 排除 ID 和創建時間
        service.save(persisted); // 使用 saveOrUpdate 或 merge
    }
    return "redirect:/list";
}

方案二:使用 merge() 替代 update() (推薦)

merge() 語義:若數據庫存在該 ID 記錄則更新,否則插入;返回持久化對象引用。不拋異常,更安全。

@Transactional
public Entity saveOrUpdateEntity(Entity entity) {
    return entityManager.merge(entity); // JPA 標準
    // 或 session.merge(entity); // Hibernate 原生
}

方案三:檢查 ID 生成策略匹配數據庫

  • MySQL: `` 對應 `AUTO_INCREMENT`。
  • Oracle: `SEQ_NAME` 對應 Sequence。
  • PostgreSQL: `` (PG 10+) 或 `sequence`。
  • 通用: `` 自動適配底層數據庫。

方案四:使用 @GeneratedValue 註解 (JPA/Hibernate 5+)

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // MySQL
// @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_gen") // Oracle
private Long id;

調試技巧

  1. 開啟 Hibernate SQL 日誌:hibernate.show_sql=true, hibernate.format_sql=true,觀察是發出 INSERT 還是 UPDATE。
  2. 斷點查看對象 id 字段值是否為預期主鍵。
  3. 檢查數據庫表結構:`SHOW CREATE TABLE` 確認主鍵自增屬性。

總結

「update 變成兩條數據」本質是 ID 丟失導致 Hibernate 誤判為新對象執行 INSERT。核心對策:保證更新對象攜帶正確主鍵 ID,推薦使用 merge() 或 JPA save() 統一處理新增/更新。

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