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

乐观锁场景下如何解锁(重载当前状态)?JPA更新抛出锁失败异常如何解决

错误原因

你遇到的ObjectOptimisticLockingFailureException是JPA乐观锁的默认触发逻辑,和事务是否提交无关:
乐观锁的核心实现规则是:实体上标注@Version的字段会作为版本标识,每次执行更新操作时,JPA生成的SQL会自动拼接版本校验逻辑:

update 你的表名 set 要更新的字段=?, version = version + 1 where id = ? and version = ?

这里where条件里的version值,是你传入save方法的实体携带的version值。第一次更新成功后,数据库里的version已经自动加1,你第二次更新时,待保存的实体携带的还是旧的version值,导致where条件匹配不到任何记录,JPA就会判定为并发修改冲突,抛出乐观锁失败异常。

常见疑问解答

关于“解锁实体”

乐观锁本质是无锁的校验机制,不存在实体被“锁住”需要主动解锁的情况。只要你更新时传入的实体携带的version和数据库当前存储的version一致,就能正常执行更新。

关于客户端传version是否为通用做法

这是RESTful接口下使用乐观锁的标准行业实践,原因如下:

  • HTTP协议本身是无状态的,服务端不会留存用户上一次GET请求获取的实体版本信息,两次请求完全独立。
  • 如果要保证乐观锁的并发冲突控制能力,就必须让客户端在更新时,把上一次查询拿到的version作为参数一起传入,服务端将该值赋值给待保存的实体,JPA的版本校验逻辑才能正常生效。
  • 如果你不想让客户端传version,也可以选择在更新逻辑里先调用findById查询数据库当前的最新实体,再把前端传入的更新字段覆盖到查出来的实体上,最后调用save方法。但这种做法会失去乐观锁的作用:如果同时有多个请求修改同一个实体,后执行的更新会直接覆盖先执行的更新结果,不会触发任何冲突提示。
落地建议

如果你的业务场景需要避免并发修改相互覆盖的问题,就保留“GET返回version、PUT传入version”的逻辑即可。如果不需要冲突控制,可以去掉@Version注解,或者更新前先查最新实体再修改保存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 12:06:07