Java中用PreparedStatement实现乐观锁的核心是在UPDATE的WHERE子句中校验版本号,若executeUpdate()返回0则更新失败,需业务层处理冲突;数据库表需含version字段,SQL为"UPDATE t SET col=?, version=version+1 WHERE id=? AND version=?",参数顺序为新值、id、旧version,且每次重试前须重新查询最新version。

Java 中用 PreparedStatement 实现乐观锁,核心是:在 UPDATE 语句的 WHERE 子句中加入版本号(如 version 字段)比对,确保更新前数据未被其他线程修改。若版本不匹配,executeUpdate() 返回 0,表示更新失败,需业务层处理冲突(如重试或提示用户)。
1. 数据库表需包含版本字段
例如 MySQL 表结构:
CREATE TABLE account (
id BIGINT PRIMARY KEY,
balance DECIMAL(10,2),
version INT DEFAULT 0
);
每次更新时,只允许 version 值等于当前读取值的记录被修改,且更新后 version 自增 1。
2. 使用 PreparedStatement 编写带版本校验的 UPDATE
关键点:SQL 中 WHERE 同时校验主键和 version;参数按顺序绑定;检查返回影响行数。
立即学习“Java免费学习笔记(深入)”;
- SQL 示例:
"UPDATE account SET balance = ?, version = version + 1 WHERE id = ? AND version = ?" - 参数顺序:新余额、id、旧版本号(即查询时拿到的 version)
- 执行后判断:
int rows = pstmt.executeUpdate();,若rows == 0,说明已被他人修改,发生乐观锁冲突
3. 完整代码示例(含重试逻辑)
以下是一个简化但可运行的场景(假设已获取到 id=100 的账户,当时 version=5,balance=1000):
String sql = "UPDATE account SET balance = ?, version = version + 1 WHERE id = ? AND version = ?";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setBigDecimal(1, new BigDecimal("1200")); // 新余额
pstmt.setLong(2, 100L); // id
pstmt.setInt(3, 5); // 旧 version
int updated = pstmt.executeUpdate();
if (updated == 0) {
throw new OptimisticLockException("更新失败:数据已被其他操作修改");
}
}
生产中常配合重试(如最多 3 次),每次重试前重新查出最新 id、balance 和 version,再构造新语句。
4. 注意事项与常见陷阱
- version 必须为整数类型(如 INT),避免浮点或字符串比较引发意外
- SELECT 时必须查出 version,不能只查业务字段,否则无法构建安全 WHERE 条件
-
UPDATE 中 version 要自增(
version = version + 1),而非设为固定值,否则下次更新仍可能成功 - 事务隔离级别无需特别提升(READ_COMMITTED 足够),乐观锁靠 SQL 条件而非数据库锁机制


















