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

不使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:42:59