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

Hibernate二次更新对象报StaleObjectStateException异常排查

问题表现

实现产品信息自由更新功能时,携带字段修改内容的更新操作第二次提交就会触发报错,错误包含提示Cannot update an object more than once,完整异常信息:

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [guru.springframework.domain.Product#4]

复现规律:

  • 提交的JSON请求体无字段修改时,多次重复提交均可正常执行
  • 请求体携带字段修改内容时,仅第一次提交能成功,第二次及后续提交直接抛出上述异常

现有业务代码

Controller层代码

@ApiOperation(value = "Update a product")
@RequestMapping(value = "/update/", method = RequestMethod.PUT, produces = "application/json")
public ResponseEntity<EntityModel<Product>> updateProduct(@RequestBody Product product){
   return productService.updateProduct(product);
}

Service实现层代码

@Override
public ResponseEntity<EntityModel<Product>> updateProduct(Product product) {
    boolean exist=productRepository.existsById(product.getProductId());
    if(exist){
        productRepository.save(product);
        return ResponseEntity.ok().body(productEntityFactory.toModel(product));
    }else {
        throw new EntityNotFoundException("There is no product with the given ID, so it can't be updated");
    }
}
问题根因

核心原因是乐观锁版本号不同步:
Product实体类配置了@Version注解实现乐观锁并发控制,现有更新逻辑直接使用前端传入的脱管态Product实体执行save操作:

  1. 第一次携带修改内容提交时,前端传入的version值和数据库存储的当前version值一致,Hibernate正常执行更新语句,同时自动将该条记录的version值+1,操作成功
  2. 第一次更新成功后,接口返回给前端的还是旧版本的实体数据,前端没有拿到更新后的最新version值
  3. 第二次携带修改内容提交时,前端传入的version值比数据库中存储的当前version值小,Hibernate判定为乐观锁冲突,直接抛出StaleObjectStateException
  4. 无修改的重复提交能正常执行,是因为Hibernate检测到实体所有字段值和数据库完全一致,不会实际执行update语句,不会触发版本校验逻辑。
修复方案

两种方案按需选择即可:

  • 方案1(推荐):更新时先查询数据库中的持久态实体,拷贝前端传入的更新字段后再执行save,返回保存后的最新实体(携带更新后的version值)给前端,前端后续更新时携带最新version值即可。修改后的Service层代码:
@Override
public ResponseEntity<EntityModel<Product>> updateProduct(Product product) {
    return productRepository.findById(product.getProductId())
            .map(persistProduct -> {
                // 拷贝字段时跳过id、version字段,避免被前端传入的旧值覆盖
                BeanUtils.copyProperties(product, persistProduct, "productId", "version");
                Product updatedProduct = productRepository.save(persistProduct);
                return ResponseEntity.ok().body(productEntityFactory.toModel(updatedProduct));
            })
            .orElseThrow(() -> new EntityNotFoundException("There is no product with the given ID, so it can't be updated"));
}

优先使用查询出的持久态实体做更新,不要直接保存前端传入的脱管态实体,可以规避绝大多数JPA/Hibernate更新场景的异常。

  • 方案2:如果业务场景不存在多端同时修改同一条产品数据的并发场景,可以直接移除Product实体类上的@Version注解关闭乐观锁,该方案会丢失并发更新冲突防护能力,非必要不推荐。

内容的提问来源于stack exchange,提问作者postFix

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:48:16