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可正常执行。
解决办法
可任选以下方案解决:
- 改用Panache的
persistOrUpdate()方法
替换仓库中的persistAndFlush()为persistOrUpdate(),该方法会自动判断实体是新实体(id为null)还是detached实体(id非空),自动执行持久化或更新操作:
@Transactional public void addPriceRequest(PriceRequest priceRequest) { priceRequest.setCreationDate(LocalDateTime.now()); priceRequest.persistOrUpdate(); // 替换原方法 }
- 使用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的问题。
- 手动清空实体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.
相关产品推荐
相关产品推荐

