Spring Boot保存ALKP实体时出现NonUniqueResultException与detached报错如何解决
Spring Boot保存ALKP实体两个报错的解决方案
报错原因分析
- 错误1
javax.persistence.NonUniqueResultException: query did not return a unique result: 2:业务代码中存在查询唯一结果的逻辑(一般是code、keyword这类字段的重复校验),当前数据库中已经存在2条符合查询条件的重复数据,查询返回多个结果触发了该异常。 - 错误2
detached entity passed to persist:直接传parentId:700时,JSON反序列化会生成一个id为700的游离态ALKP对象,该对象没有被JPA持久化上下文管理,直接作为关联属性保存新实体时就会触发该错误。
具体解决步骤
1. 修复请求参数格式
你推测的参数结构是正确的,将Postman请求体调整为如下格式,保证反序列化时可以正确封装parentId的id属性:
{ "title": "FeMale", "keyword": "GENDER_FEMALE", "slNo": 2, "active": true, "code": "MC-00209", "fullName": "FEMALE", "parentId": { "id": 700 } }
注意:你的实体类里布尔属性名为
isActive,请求参数传的是active,如果出现属性赋值失败的情况,可以在isActive字段上加@JsonProperty("active")注解适配参数名。
2. 调整业务层保存逻辑
不能直接用请求反序列化得到的parentId对象做关联保存,需要先根据parentId的id从数据库查询对应的持久化实体,再赋值给新的ALKP对象,参考代码如下:
@Service public class ALKPService { @Autowired private ALKPRepository alkpRepository; @Transactional public ALKP saveALKP(ALKP reqAlkp) { // 处理父级实体 if (reqAlkp.getParentId() != null && reqAlkp.getParentId().getId() != null) { // 从数据库查询父级实体得到持久态对象 ALKP parent = alkpRepository.findById(reqAlkp.getParentId().getId()) .orElseThrow(() -> new RuntimeException("父级ALKP不存在,id:" + reqAlkp.getParentId().getId())); reqAlkp.setParentId(parent); } // 保存新实体 return alkpRepository.save(reqAlkp); } }
3. 处理非唯一结果异常
先清理数据库中的重复数据:
- 执行SQL检查keyword字段重复:
SELECT * FROM ALKP WHERE KEYWORD = 'GENDER_FEMALE' - 执行SQL检查code字段重复:
SELECT * FROM ALKP WHERE code = 'MC-00209'
删除冗余的重复数据后,可以给keyword、code字段加唯一索引避免后续产生重复数据,同时把业务代码中getSingleResult()的调用改成列表查询,自己判断结果是否唯一,避免直接抛出异常。
可选优化:用DTO接收请求参数
不要直接用实体类接收请求参数,单独定义DTO类接收参数,手动赋值给实体类,减少反序列化带来的异常问题:
// ALKP请求DTO public class ALKPReqDTO { private String title; private String keyword; private String code; private Long slNo; private String fullName; private boolean active; private Long parentId; // 直接传父级id即可,不需要传对象 }
用这种方式请求参数可以简化为直接传"parentId":700,更符合前后端交互的常规习惯。
内容的提问来源于stack exchange,提问作者Bulbul Ahmed
相关产品推荐
相关产品推荐

