Quarkus Jakarta Persistence:已持久化实体更新是否需调用persist()?
关于Quarkus中JPA实体更新的两个疑问解答
先看你提到的官方更新示例代码:
@PUT @Path("/{id}") @Transactional public Person update(Long id, Person person) { Person entity = Person.findById(id); if(entity == null) { throw new NotFoundException(); } // map all fields from the person parameter to the existing entity entity.name = person.name; return entity; }
疑问1:修改已持久化的实体时,是否需要再次调用persist()方法?
不需要,原因如下:
- 通过
Person.findById(id)加载的entity处于托管状态(Managed),在@Transactional注解的方法内,JPA(Quarkus默认用Hibernate实现)会自动跟踪这个实体的所有修改。 - 当事务提交时,JPA会自动检测实体的变化,把更新同步到数据库,完全不需要手动调用
persist()。 persist()的作用是将**新创建的游离实体(Detached)**纳入JPA托管上下文,让它变成持久化状态。对已经托管的实体调用persist()不仅没用,某些场景下还可能引发异常。
疑问2:为什么需要字段映射,不能直接调用传入的person对象的persist()方法?
直接调用传入的person.persist()会引发问题,核心原因有几点:
- 传入的
person是**游离状态(Detached)**的对象(可能是前端DTO转成的实体类,或是之前加载后脱离了事务上下文的对象),并非当前事务中托管的实体。 - 直接调用
persist()的话,JPA会把这个对象当成新实体插入数据库。如果传入的person带有和数据库现有记录相同的id,会触发主键冲突异常;如果id是自增类型,甚至会生成一条全新的记录,完全达不到更新的目的。 - 传入的
person可能缺失数据库实体的完整状态:比如乐观锁版本字段、关联的其他实体、数据库默认值生成的字段等。直接替换会导致这些数据丢失,引发业务逻辑错误或持久化异常。
所以官方示例的做法才是正确的:先加载数据库中对应的托管实体,再将传入对象的字段值映射过去,依靠JPA的自动脏检查机制完成更新。
内容的提问来源于stack exchange,提问作者Fexl
相关产品推荐
相关产品推荐

