Java中JDBC实现乐观锁需三步:1.表加version字段并设默认值0;2.查询时读取当前version;3.更新时WHERE校验原version且SET中version+1,若executeUpdate返回0则抛并发异常。

Java 中使用 JDBC 实现乐观锁的版本号校验,核心是通过 SQL 的 WHERE 条件中带上 version 字段,在执行 UPDATE 时检查当前数据库中的 version 是否与应用读取的一致。若不一致(即影响行数为 0),说明数据已被其他线程/事务修改,应拒绝本次更新并抛出并发异常。
1. 数据库表需包含 version 字段
确保对应表有 version 列(通常为 INT 或 BIGINT 类型,默认值为 0):
ALTER TABLE user ADD COLUMN version INT NOT NULL DEFAULT 0;
每次更新时,version 自增 1,且必须在 WHERE 子句中校验原始值。
2. 查询时读取当前 version 值
使用 SELECT 获取数据的同时,一并查出 version:
立即学习“Java免费学习笔记(深入)”;
String sql = "SELECT id, name, age, version FROM user WHERE id = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setLong(1, userId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setAge(rs.getInt("age"));
user.setVersion(rs.getInt("version")); // 关键:保存当前版本号
return user;
}
}
3. 更新时校验 version 并自增
UPDATE 语句必须同时满足两个条件:主键匹配 + version 等于查询时的值。成功则 version +1;失败则影响行为 0:
String sql = "UPDATE user SET name = ?, age = ?, version = version + 1 WHERE id = ? AND version = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, user.getName());
ps.setInt(2, user.getAge());
ps.setLong(3, user.getId());
ps.setInt(4, user.getVersion()); // 使用查询时拿到的旧 version
int affected = ps.executeUpdate();
if (affected == 0) {
throw new OptimisticLockException("数据已被修改,请刷新后重试");
}
}
- 注意:SQL 中
version = version + 1是原子操作,由数据库保证安全 - WHERE 中的
version = ?必须传入最初读取的值,不能用新值 - JDBC 不会自动抛出异常,需手动判断
executeUpdate()返回值
4. 封装成可复用的工具逻辑(可选)
可将 version 校验封装为通用方法,例如在 DAO 层增加:
public boolean updateWithVersion(String sql, Object... params) throws SQLException {
try (PreparedStatement ps = conn.prepareStatement(sql)) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
return ps.executeUpdate() > 0;
}
}
// 调用示例:
boolean success = updateWithVersion(
"UPDATE user SET name=?, age=?, version=version+1 WHERE id=? AND version=?",
newUser.getName(), newUser.getAge(), userId, oldVersion
);
if (!success) throw new OptimisticLockException(...);


















