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

Quarkus应用OpenAPI UI报Detached entity passed to persist但Postman正常

问题描述

我正在开发一个Quarkus应用,需要将PriceRequest实体存储到数据库中,实体简化代码如下:

@Entity
@Table(name = "t_price_request")
public class PriceRequest extends PanacheEntity {
  @Column
  public String buildingType;
  @Column
  public int squareMeters;
}

通过POST请求调用仓库的addPriceRequest方法保存实体,仓库代码如下:

@ApplicationScoped
public class PriceRequestRepository implements PanacheRepository<PriceRequest> {
    @Transactional
    public void addPriceRequest(PriceRequest priceRequest) {
        priceRequest.setCreationDate(LocalDateTime.now());
        priceRequest.persistAndFlush();
    }

    @Transactional
    public PriceRequest findPriceRequestById(Long id) {
        return findById(id);
    }

    @Transactional
    public List<PriceRequest> findAllPriceRequests() {
        return findAll().stream().toList();
    }
}

使用Postman测试一切正常,但通过OpenAPI UI测试时出现以下错误:

HTTP Request to /price-request failed, error id: a5caae7f-5daa-49ef-8358-0e57b9895a1a-6: org.hibernate.PersistentObjectException: detached entity passed to persist: be.backeit.domain.PriceRequest
        at org.hibernate.event.internal.DefaultPersistEventListener.persist(DefaultPersistEventListener.java:88)

请问代码存在什么问题?是否需要添加额外注解?

问题分析与解决方案

问题原因

报错核心是Hibernate收到处于detached状态的实体,却调用了仅适用于新实体的persist操作:

  • PanacheEntity自带id字段,OpenAPI UI生成请求示例时会自动包含该字段,提交请求时实体被赋予非空id,Hibernate判定这是一个已持久化、当前脱离会话的detached实体。
  • Postman测试时未传入id,实体id为null,Hibernate判定为新实体,persistAndFlush可正常执行。

解决办法

可任选以下方案解决:

  1. 改用Panache的persistOrUpdate()方法
    替换仓库中的persistAndFlush()为persistOrUpdate(),该方法会自动判断实体是新实体(id为null)还是detached实体(id非空),自动执行持久化或更新操作:
@Transactional
public void addPriceRequest(PriceRequest priceRequest) {
    priceRequest.setCreationDate(LocalDateTime.now());
    priceRequest.persistOrUpdate(); // 替换原方法
}
  1. 使用DTO隔离实体与API请求
    创建专门的请求DTO(如CreatePriceRequestDTO),不包含id字段,接收前端请求后转换为PriceRequest实体:
// DTO类
public class CreatePriceRequestDTO {
    public String buildingType;
    public int squareMeters;
}

// 仓库方法修改为接收DTO
@Transactional
public void addPriceRequest(CreatePriceRequestDTO dto) {
    PriceRequest priceRequest = new PriceRequest();
    priceRequest.buildingType = dto.buildingType;
    priceRequest.squareMeters = dto.squareMeters;
    priceRequest.setCreationDate(LocalDateTime.now());
    priceRequest.persistAndFlush();
}

这种方式符合分层架构设计,避免实体直接暴露给API层,从根源上杜绝前端传入id的问题。

  1. 手动清空实体id
    若坚持直接使用实体接收请求,可在保存前手动将id设为null,确保Hibernate将其视为新实体:
@Transactional
public void addPriceRequest(PriceRequest priceRequest) {
    priceRequest.id = null; // 清空id
    priceRequest.setCreationDate(LocalDateTime.now());
    priceRequest.persistAndFlush();
}

内容的提问来源于stack exchange,提问作者Jan D.M.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 22:14:53