數據庫主鍵配置為自增 (MySQL AUTO_INCREMENT, Oracle Sequence, SQL Server IDENTITY)。原有 1 條數據,執行 Hibernate update() 後,數據庫變成 2 條,主鍵分別為原 ID 和新自增 ID。
Hibernate Session.update() 僅用於持久化對象 (已有 ID,且數據庫存在該記錄)。若對象處於遊離狀態 且 ID 為空/默認值 (0/null),Hibernate 會誤以為是瞬時對象,執行 save() 邏輯插入新記錄。
常見觸發場景:
update(entity)。<!-- 數據庫自增:推薦 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>
// 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 原生
}
@GeneratedValue 註解 (JPA/Hibernate 5+)@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // MySQL
// @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_gen") // Oracle
private Long id;
hibernate.show_sql=true, hibernate.format_sql=true,觀察是發出 INSERT 還是 UPDATE。id 字段值是否為預期主鍵。「update 變成兩條數據」本質是 ID 丟失導致 Hibernate 誤判為新對象執行 INSERT。核心對策:保證更新對象攜帶正確主鍵 ID,推薦使用 merge() 或 JPA save() 統一處理新增/更新。