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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 12:30:53