不使用jOOQ Records实现乐观锁,如何区分更新失败的原因?
解决方案
要区分「ID不存在」和「版本不一致」这两种更新失败的情况,可以按以下步骤处理:
核心思路
更新失败(影响行数为0)时,在同一个事务内查询目标ID的记录,根据查询结果判断具体原因:
- 查不到记录 → ID不存在
- 查到记录但版本不匹配 → 乐观锁冲突
代码实现
首先定义两个细分异常,替代原来的SomeException,方便调用方区分处理:
public class EntityNotFoundException extends RuntimeException { public EntityNotFoundException(String message) { super(message); } } public class OptimisticLockException extends RuntimeException { public OptimisticLockException(String message) { super(message); } }
然后修改更新方法,加上事务保证查询和更新的一致性:
import org.springframework.transaction.annotation.Transactional; @Transactional public void update(SomeEntity value) { int updatedRows = dsl.update(TABLE_NAME) .set(TABLE_NAME.FIELD1, value.getField1()) .set(TABLE_NAME.LAST_UPDATE, OffsetDateTime.now()) .set(TABLE_NAME.VERSION, TABLE_NAME.VERSION.add(1)) .where(TABLE_NAME.ID.eq(value.getId()) .and(TABLE_NAME.VERSION.eq(value.getVersion()))) .execute(); if (updatedRows > 0) { return; } // 检查更新失败的原因 var record = dsl.selectFrom(TABLE_NAME) .where(TABLE_NAME.ID.eq(value.getId())) .fetchOne(); if (record == null) { throw new EntityNotFoundException("ID为" + value.getId() + "的实体不存在"); } else { throw new OptimisticLockException( "版本不匹配:预期版本" + value.getVersion() + ",实际版本" + record.getVersion() ); } }
注意事项
- 必须用
@Transactional包裹方法,确保更新和后续查询在同一个事务中,避免中间状态被其他线程修改导致判断错误。 - 乐观锁冲突通常是可重试的场景,而实体不存在不需要重试,调用方可以根据不同异常做差异化处理。
- 如果你的数据库支持返回更新后的记录(比如PostgreSQL、MySQL 8.0+),也可以用
dsl.update(...).returning(TABLE_NAME.VERSION).fetchOne()替代execute(),直接获取更新结果,减少一次查询,但本质逻辑和上面一致。
内容的提问来源于stack exchange,提问作者Ilya
相关产品推荐
相关产品推荐

