JPA:identifier of an instance of (entity )was altered from 数字 to null;

解决JPA主键更新异常问题
博客内容涉及了一个在Java项目中使用JPA时遇到的异常:identifierofaninstance...。问题在于尝试更新一个实体的主键,这是不被允许的。解决方案是在复制实体属性之前,先将关联实体设为null,然后设置主键为空,再进行保存操作,从而避免主键更新的错误。

项目场景:

报错如下

identifier of an instance of com.pcitc.swm.pm.SolidWasteStorRec was altered from 4 to null; nested exception is org.hibernate.HibernateException: identifier of an instance of com.pcitc.swm.pm.SolidWasteStorRec was altered from 4 to null

问题描述:

在这里插入图片描述

SolidWasteStorRec solidWasteStorRecOut = storPointSWPrcDeta.getSolidWasteStorRecIn();
                StorPointInitialStock storPointInitialStock = storPointSWPrcDeta.getStorPointInitialStock();
                SolidWasteStorRec solidWasteStorRecOutResult = null;
                if (solidWasteStorRecOut !=null){
                    SolidWasteStorRec solidWasteStorRec1 = new SolidWasteStorRec();
                    solidWasteStorRecOut.setSolidWasteStorRecId(null);
                    EPBeanUtils.copyProperties(solidWasteStorRecOut,solidWasteStorRec1);
                    solidWasteStorRec1.setWeight(storPointSWPrcDeta.getFactPrcQty());
                    solidWasteStorRec1.setStorType(SWMEnum.StorTypeEnum.exWarehouse.getIndex());
                    solidWasteStorRec1.setCrtTime(new Date());
                    solidWasteStorRec1.setCrtUserCode(new UserContextUtil().getUserId());
                    solidWasteStorRec1.setCrtUserName(new UserContextUtil().getUserName());
                    solidWasteStorRec1.setOutOrInTime(new Date());
                    solidWasteStorRecOutResult = solidWasteStorRecRepository.saveAndFlush(solidWasteStorRecOut);
}

原因分析:

JPA :这个错误是由于在一个事务中更新了主键,而主键是不能被更新的.

解决方案:

再copy之前先赋值(把关联表也的entity也设为null),然后再把主键设置为空再保存

SolidWasteStorRec solidWasteStorRecOut = storPointSWPrcDeta.getSolidWasteStorRecIn();
                StorPointInitialStock storPointInitialStock = storPointSWPrcDeta.getStorPointInitialStock();
                SolidWasteStorRec solidWasteStorRecOutResult = null;
                if (solidWasteStorRecOut !=null){
                    SolidWasteStorRec solidWasteStorRec1 = new SolidWasteStorRec();
                    solidWasteStorRecOut.setCompanyHazaWaste(null);
                    solidWasteStorRecOut.setCommonSolidWaste(null);
                    solidWasteStorRecOut.setSolidWasteApply(null);
                    solidWasteStorRecOut.setHazaWasteDtl(null);
                    solidWasteStorRecOut.setPoltSource(null);
                    solidWasteStorRecOut.setSwTempStorEqp(null);
                    solidWasteStorRecOut.setWeight(storPointSWPrcDeta.getFactPrcQty());
                    solidWasteStorRecOut.setStorType(SWMEnum.StorTypeEnum.exWarehouse.getIndex());
                    solidWasteStorRecOut.setCrtTime(new Date());
                    solidWasteStorRecOut.setCrtUserCode(new UserContextUtil().getUserId());
                    solidWasteStorRecOut.setCrtUserName(new UserContextUtil().getUserName());
                    solidWasteStorRec1.setOutOrInTime(new Date());
                    EPBeanUtils.copyProperties(solidWasteStorRecOut,solidWasteStorRec1);
                    solidWasteStorRec1.setSolidWasteStorRecId(null);
                    solidWasteStorRecOutResult = solidWasteStorRecRepository.saveAndFlush(solidWasteStorRec1);
                }
<think>我们正在处理用户关于Spring Data JPAHibernate中出现的JpaSystemException异常,具体是标识符(identifier)被修改(altered)的问题。 根据经验,这种异常通常发生在实体对象的持久化状态中,当尝试修改实体的主键(标识符)时,Hibernate会抛出此异常。因为主键一旦设定就不应该改变。 可能的原因: 1. 在实体类中,主键字段(通常用@Id标注)被意外修改。 2. 在实体类中,可能使用了可变类型(如String)作为主键,并在实体处于持久化状态时修改了它的值。 解决方案: 1. 确保在实体对象被持久化(例如,通过save方法保存到数据库)后,不再修改其主键字段。 2. 如果主键是自动生成的(如使用@GeneratedValue),则不应该手动设置或修改它。 3. 检查代码中是否有地方在实体被管理(managed)状态时修改了主键。 另外,注意Hibernate的session状态:当一个实体被关联到Hibernate session(例如,通过getOne或findById方法获取)时,它处于持久化状态。此时修改其主键会导致异常。 示例代码: 假设有一个实体类User: ```java @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; // getters and setters } ``` 在以下代码中,如果尝试修改id,就会抛出异常: ```java @Service public class UserService { @Autowired private UserRepository userRepository; public void updateUserName(Long userId, String newName) { User user = userRepository.findById(userId).orElseThrow(); // 错误:尝试修改主键 user.setId(100L); // 这会导致JpaSystemException: identifier altered user.setName(newName); userRepository.save(user); } } ``` 因此,修复方法就是避免修改主键。 如果用户确实需要修改主键,那么正确的做法是先删除旧记录,再插入一条新记录(使用新的主键值)。但注意,这可能会带来关联数据的问题(如外键约束)。 此外,如果用户遇到的是级联操作中相关实体的主键被修改,也需要检查相关实体的主键是否被修改。 根据以上分析,我们给出回答。</think>### Spring Data JPA Hibernate 标识符被修改异常分析与解决 当使用 Spring Data JPAHibernate 时,遇到 `JpaSystemException: identifier of an instance was altered` 异常,通常表示**实体对象的主键(ID)在持久化状态中被非法修改**。以下是详细分析和解决方案: --- ### 原因分析 1. **主键不可变性** Hibernate 要求实体主键在对象被持久化后(关联到 Session 或 EntityManager)保持不可变。尝试修改主键会触发此异常[^1]。 2. **典型场景** ```java @Entity public class User { @Id // 主键字段 private Long id; // 其他字段... } // 错误代码示例:修改持久化对象的主键 User user = userRepository.findById(1L).get(); user.setId(2L); // 触发异常! userRepository.save(user); ``` 3. **根本原因** Hibernate 使用主键跟踪实体状态。修改主键会破坏 Hibernate 的内部标识机制,导致状态不一致[^1]。 --- ### 解决方案 #### ✅ 方法 1:避免直接修改主键 - **原则**:主键应在对象创建时设置,后续永不修改 - **正确做法**: ```java // 创建新对象代替修改主键 User newUser = new User(); newUser.setId(2L); // 仅允许在瞬态(未持久化)状态下设置ID newUser.setName("New Name"); userRepository.save(newUser); // 删除旧对象(如需替换) userRepository.deleteById(1L); ``` #### ✅ 方法 2:使用业务键代替主键修改 若需"修改ID",实际应通过业务逻辑实现: ```java // 1. 创建新实体 User newUser = copyProperties(oldUser); newUser.setId(newId); // 2. 保存新实体 userRepository.save(newUser); // 3. 删除旧实体 userRepository.delete(oldUser); // 4. 更新关联关系(需事务管理) orderRepository.updateUserId(oldUser.getId(), newUser.getId()); ``` #### ✅ 方法 3:检查级联操作 当主键被级联操作意外修改时: ```java @Entity public class Order { @Id private Long id; @ManyToOne @JoinColumn(name = "user_id") private User user; // 关联实体 } // 错误:修改关联实体的主键 Order order = orderRepository.findById(orderId).get(); order.getUser().setId(newId); // 触发异常! ``` **修复**:修改关联关系而非主键: ```java User newUser = userRepository.findById(newId).get(); order.setUser(newUser); // 正确:替换关联引用 ``` --- ### 预防措施 1. **实体设计规范**: - 主键字段添加 `final` 修饰符(如适用) - 使用 `@GeneratedValue` 自动生成不可变ID ```java @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private final Long id; // final 防止修改 ``` 2. **代码审查重点**: - 检查所有 `setId()` 调用是否发生在持久化对象上 - 使用 `@Immutable` 注解标记不可变实体 ```java @Entity @Immutable // Hibernate 特殊注解 public class AuditLog { ... } ``` 3. **Hibernate 配置增强**: ```properties # 开启调试日志定位非法修改 spring.jpa.properties.hibernate.event.merge.entity_state_logging=true ``` > **关键原则**:数据库主键对应 OOP 中的对象标识,修改它等同于创建新对象。业务上需要"修改ID"的需求,应通过创建新实体+迁移数据实现[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值