PostgreSQL中JPA更新报错‘identifier of an instance...’求解决
解决Hibernate "identifier of an instance of ...was altered from ..." 更新报错问题
我搜了大量关于“identifier of an instance of ...was altered from ...”的相关内容,没找到能解决我问题的方案。用的PostgreSQL数据库,对应的数据表只有id_type和libelle两列。创建、读取、删除功能都正常,只有更新功能失效,相关代码如下:
Model层代码
package com.stev.pillecons.pilleCons.models; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity(name = "type_pille") @JsonIgnoreProperties({"hibernateLazyInitializer","handler"}) public class LePille { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) private int id_type; private String libelle; public LePille(){} public String getLibelle() { return libelle; } public void setLibelle(String libelle) { this.libelle = libelle; } public int getId_type() { return id_type; } public void setId_type(int id_type) { this.id_type = id_type; } }
Service层代码
@Override public LePille updatePille(Integer id, LePille Sourcepille) { Optional<LePille> existingSession = pilleRepo.findById(id); if (existingSession.isPresent()) { LePille Targetpile = existingSession.get(); BeanUtils.copyProperties(Sourcepille, Targetpile); return pilleRepo.saveAndFlush(Targetpile); } else { throw new PilleException("pille not found"); } }
Controller层代码
@RequestMapping(value = "{id}", method = RequestMethod.PUT) public ResponseEntity update(@PathVariable Integer id, @RequestBody LePille session) { LePille updPille = pilleService.updatePille(id, session); return new ResponseEntity<LePille>(updPille, HttpStatus.OK); }
调试信息
通过Postman发起PUT请求,传入的数据是{"id_type":10,"libelle":"dsf"},调试时发现:
SourcePille的值为{"id_type":0,"libelle":"popo"}Targetpile的值为{"id_type":10,"libelle":"dsf"}
问题原因
报错的核心是Hibernate不允许修改持久化实体的主键值,而你的代码中BeanUtils.copyProperties会把传入对象的id_type(主键)复制到从数据库查询出的实体对象上,导致原有主键被覆盖,触发Hibernate的主键变更检测报错。
解决方案
方案1:排除主键字段复制
修改BeanUtils.copyProperties的调用,明确排除主键字段id_type,只复制需要更新的业务字段:
BeanUtils.copyProperties(Sourcepille, Targetpile, "id_type");
方案2:移除主键的setter方法(推荐)
因为你的主键采用IDENTITY自增策略,业务上不需要修改主键,直接删除实体类中的setId_type方法,从根源上避免主键被修改:
// 删除这个方法 public void setId_type(int id_type) { this.id_type = id_type; }
方案3:手动复制更新字段
放弃使用BeanUtils,手动复制需要更新的字段,更安全可控:
LePille Targetpile = existingSession.get(); Targetpile.setLibelle(Sourcepille.getLibelle()); // 仅更新libelle字段 return pilleRepo.saveAndFlush(Targetpile);
额外优化
Controller中同时接收了路径参数id和请求体中的id_type,建议以路径参数id为准,忽略请求体中的主键值,避免出现参数不一致的情况。比如在Controller中可以直接设置:
session.setId_type(id);
或者在Model层通过@JsonIgnore注解忽略请求体中的id_type字段,确保主键只由数据库自增生成。
内容的提问来源于stack exchange,提问作者user609511
相关产品推荐
相关产品推荐

