JDBC 事务中如何保证操作的幂等性
在 jdbc 事务中实现幂等性的核心做法是引入业务唯一键(如订单号或请求 id)并在数据库层面建立唯一索引。通过 insert ... on duplicate key update 语法或先查询后更新的逻辑,确保同一业务请求被重复提交时不会产生副作用。这样即使网络重试导致 sql 执行多次,最终数据状态依然保持一致。

使用唯一索引与 Upsert 语句处理重复提交
利用数据库的唯一约束拦截重复操作是最可靠的方案。当应用层捕获到 DuplicateKeyException 时,说明该笔业务已经处理过,此时只需确认当前状态即可返回成功,无需再次修改数据。

你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
import java.sql.*;
public class IdempotentJdbcExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "password";
// 模拟业务请求 ID
String bizId = "ORDER_001";
int amount = 100;
try (Connection conn = DriverManager.getConnection(url, user, password)) {
conn.setAutoCommit(false);
// 1. 尝试插入,若 biz_id 已存在则更新金额
String sql = "INSERT INTO orders (biz_id, amount, status) VALUES (?, ?, 'PAID') " +
"ON DUPLICATE KEY UPDATE amount = VALUES(amount), status = 'PAID'";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, bizId);
pstmt.setInt(2, amount);
pstmt.executeUpdate();
}
conn.commit();
System.out.println("Transaction committed successfully.");
} catch (SQLException e) {
System.err.println("Transaction failed: " + e.getMessage());
}
}
}
基于版本号的条件更新防止并发覆盖
对于需要严格顺序处理的场景,可以使用乐观锁机制。每次更新时检查版本号是否匹配,只有匹配时才进行更新并递增版本。如果版本号不匹配,说明数据已被其他事务修改,此时应视为非幂等或需要重新获取最新状态。

import java.sql.*;
public class OptimisticLockExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "password";
String bizId = "ORDER_002";
int newAmount = 200;
int currentVersion = 1; // 假设当前已知版本为 1
try (Connection conn = DriverManager.getConnection(url, user, password)) {
conn.setAutoCommit(false);
// 1. 先查询当前版本
String selectSql = "SELECT version FROM orders WHERE biz_id = ?";
Integer dbVersion = null;
try (PreparedStatement pstmt = conn.prepareStatement(selectSql)) {
pstmt.setString(1, bizId);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
dbVersion = rs.getInt("version");
}
}
if (dbVersion == null) {
System.out.println("Record not found.");
return;
}
// 2. 条件更新:仅当版本一致时才更新
String updateSql = "UPDATE orders SET amount = ?, version = version + 1 WHERE biz_id = ? AND version = ?";
try (PreparedStatement pstmt = conn.prepareStatement(updateSql)) {
pstmt.setInt(1, newAmount);
pstmt.setString(2, bizId);
pstmt.setInt(3, currentVersion);
int rowsAffected = pstmt.executeUpdate();
if (rowsAffected == 0) {
System.out.println("Update failed due to version conflict. Data may have been modified.");
conn.rollback();
} else {
conn.commit();
System.out.println("Update successful with optimistic lock.");
}
}
} catch (SQLException e) {
System.err.println("Error: " + e.getMessage());
}
}
}
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










