You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 08:51:03