java并发下乐观锁冲突处理核心是捕获特定异常并转为业务可识别的全局异常,mybatis plus需手动判行数0后抛自定义异常,jpa抛optimisticlockexception,统一用@exceptionhandler拦截并返回409状态码。

Java 中处理并发请求下的乐观锁冲突,核心是捕获特定异常并统一转化为业务可识别的全局异常,同时避免事务回滚失控或重复提交问题。
识别乐观锁冲突异常类型
MyBatis Plus、JPA(Hibernate)等主流 ORM 框架在更新失败时抛出的异常并不统一,需按实际框架确认:
-
MyBatis Plus:执行
updateById或带@Version的更新时,若数据库 version 不匹配,不会抛异常,而是返回影响行数0—— 需主动判断并 throw 自定义异常 -
Spring Data JPA / Hibernate:默认抛出
OptimisticLockException(继承自PersistenceException),属于受检异常的运行时异常,会被事务管理器识别并触发回滚 -
原生 JDBC 或自定义 SQL:需手动在 SQL 中校验 version 字段,用
UPDATE ... SET version = version + 1 WHERE id = ? AND version = ?,再根据executeUpdate()返回值是否为 1 来判断是否更新成功
定义业务级乐观锁异常
创建一个明确语义的运行时异常,便于全局捕获和前端识别:
public class OptimisticLockFailException extends RuntimeException {
public OptimisticLockFailException() {
super("数据已被其他操作修改,请刷新后重试");
}
public OptimisticLockFailException(String message) {
super(message);
}
}
该异常不参与事务控制逻辑(即不强制回滚),由上层显式处理,更灵活。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
全局异常处理器统一拦截
使用 @ControllerAdvice 拦截两类情况:
- 直接抛出的
OptimisticLockException(JPA 场景) - 手动 throw 的
OptimisticLockFailException(MyBatis Plus 或自定义场景)
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler({OptimisticLockException.class, OptimisticLockFailException.class})
public ResponseEntity<apiresponse> handleOptimisticLock(Exception e) {
return ResponseEntity.status(409).body(
ApiResponse.fail("409", "版本冲突", "请刷新页面后重试")
);
}
}</apiresponse>
HTTP 状态码推荐用 409 Conflict,语义准确;避免用 400 或 500,便于前端做针对性重试或提示。
事务与重试的协同要点
乐观锁异常发生时,事务通常已标记为 rollback-only,需注意:
- 不要在同一个事务方法内“捕获后重试”,否则会报
TransactionSystemException: Could not commit transaction - 如需自动重试(如幂等写入),应将操作拆分为独立事务方法,并用
@Retryable(Spring Retry)或手动循环 + 新事务(Propagation.REQUIRES_NEW)实现 - 前端配合:提交失败后主动 reload 数据、重置表单 version 值,或引导用户确认是否覆盖编辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










