JDBC中用版本号实现乐观锁,核心是SQL层校验:表需含INT/BIGINT型version字段(初值0或1),查询时显式SELECT version并存入对象,更新时用UPDATE ... SET ... version=version+1 WHERE id=? AND version=?,若executeUpdate()返回0则抛OptimisticLockException提示冲突。

在 JDBC 中用版本号实现乐观锁,核心是把校验逻辑“下推”到 SQL 层,靠 UPDATE 语句的 WHERE 条件 + 影响行数判断 来识别并发冲突,Java 层只做轻量 if 分支处理。
数据库表必须带 version 字段
建表时添加整型版本列(推荐 INT 或 BIGINT),初始值设为 0 或 1:
- 不要用时间戳替代 version —— 时间可能重复、回拨,无法保证单调递增
- 字段名建议统一为
version,便于 ORM 映射和团队理解 - 确保该列参与所有带乐观锁语义的 UPDATE 操作
读取时一并获取 version 值
查询数据必须显式 SELECT version 字段,不能靠默认值或构造器填充:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 例如:
SELECT id, name, balance, version FROM account WHERE id = ? - 将查到的 version 存入 Java 对象(如
Account.setVersion(rs.getInt("version"))) - 后续更新必须使用这个原始 version 值作为比对基准
更新时用 WHERE version = ? 校验并原子递增
UPDATE 语句要同时满足三个条件:主键匹配、版本号未变、版本号自增:
立即学习“Java免费学习笔记(深入)”;
- SQL 示例:
UPDATE account SET balance = ?, version = version + 1 WHERE id = ? AND version = ? - 执行后检查
PreparedStatement.executeUpdate()返回值 -
if (rowsAffected == 1)→ 成功,本地对象 version 也应加 1,保持内存与 DB 一致 -
else→ 冲突,说明已有其他事务抢先更新,此时 version 已变,当前修改被拒绝
冲突后不静默覆盖,要明确反馈
失败分支不是异常兜底,而是业务决策点:
- 抛出
OptimisticLockException,由上层捕获后提示用户“数据已被他人修改,请刷新重试” - 或返回标准错误码(如 HTTP 409 Conflict),配合前端做自动 reload
- 不建议在 JDBC 层自动重试 —— 单次更新失败通常意味着业务意图已过期,重试可能造成逻辑错误

















