Quarkus中PanacheEntity记录仅能更新一次问题求助
Quarkus中Hibernate Reactive多次更新数据库报StaleObjectStateException的解决方法
我在学习Quarkus时遇到数据库记录无法多次更新的问题,使用
merge()方法更新记录,首次更新正常,再次更新时报错:"details": "Error id b98d37fe-4753-409d-b46a-efe32cb84419-1, org.hibernate.HibernateException: java.util.concurrent.CompletionException: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.example.Services.User.User#2]"原更新代码:
@WithTransaction public Uni<User> update(User user) { return findById(user.id) .chain(u -> User.getSession()) .chain(s -> s.merge(user)); }
问题原因
这个错误是乐观锁冲突导致的:你传入的User对象属于脱离Hibernate会话的detached状态,第一次更新后数据库中的实体版本号已递增,但后续更新仍使用旧版本号的对象实例,Hibernate检测到版本不匹配,就会抛出该异常。同时原代码中先查询实体再获取会话合并的逻辑完全多余,反而加剧了状态管理混乱。
解决方案
方案1:使用托管实体更新(推荐)
直接加载数据库中的managed(托管)状态实体,修改属性后Hibernate会自动在事务提交时完成更新,无需手动调用merge:
@WithTransaction public Uni<User> update(User user) { return findById(user.id) .chain(managedUser -> { // 将传入对象的属性复制到托管实体 managedUser.setName(user.getName()); managedUser.setEmail(user.getEmail()); // 按需添加其他属性更新逻辑 return Uni.createFrom().item(managedUser); }); }
这种方式每次更新都基于数据库最新的托管实体,版本号会自动同步,不会出现冲突。
方案2:正确使用merge方法
如果必须基于传入的detached对象更新,确保merge操作基于当前会话的最新状态,且后续更新使用merge返回的托管实体:
@WithTransaction public Uni<User> update(User user) { return User.getSession() .chain(session -> session.merge(user)); }
⚠️ 注意:后续若需再次更新该实体,必须使用merge返回的对象,而非最初传入的detached对象——前者是托管状态,版本号会随更新自动递增。
额外注意事项
- 若
User实体使用了乐观锁(添加了@Version注解),务必保证每次更新时对象的版本号与数据库一致,否则必然触发该异常。 - 避免重复使用同一个
detached对象实例进行多次更新,每次更新都应基于最新从数据库加载的实体,或merge返回的托管实体。
内容的提问来源于stack exchange,提问作者niao
相关产品推荐
相关产品推荐

