本文目录导读:

我为您准备了一个完整的JDBC事务管理案例,包含银行转账的经典场景,并提供两种实现方式。
数据库准备
-- 创建数据库
CREATE DATABASE bank_db;
USE bank_db;
-- 创建账户表
CREATE TABLE accounts (
id INT PRIMARY KEY AUTO_INCREMENT,
account_name VARCHAR(50) NOT NULL,
balance DECIMAL(10,2) NOT NULL
);
-- 插入测试数据
INSERT INTO accounts (account_name, balance) VALUES ('张三', 5000.00);
INSERT INTO accounts (account_name, balance) VALUES ('李四', 3000.00);
事务管理案例(转账操作)
方案1:基础版 - 手动事务管理
import java.sql.*;
public class TransactionDemo {
// 数据库连接参数
private static final String URL = "jdbc:mysql://localhost:3306/bank_db";
private static final String USER = "root";
private static final String PASSWORD = "your_password";
public static void main(String[] args) {
transferFunds("张三", "李四", 1000.00);
}
/**
* 转账操作(含事务管理)
*/
public static void transferFunds(String fromAccount, String toAccount, double amount) {
Connection conn = null;
Statement stmt = null;
try {
// 1. 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 获取连接
conn = DriverManager.getConnection(URL, USER, PASSWORD);
// 3. 关闭自动提交,开启事务
conn.setAutoCommit(false);
stmt = conn.createStatement();
// 4. 执行转账操作
// 4.1 扣款操作
String deductSQL = String.format(
"UPDATE accounts SET balance = balance - %.2f WHERE account_name = '%s'",
amount, fromAccount
);
int deductResult = stmt.executeUpdate(deductSQL);
// 检查扣款是否成功
if (deductResult == 0) {
throw new RuntimeException("转出账户不存在");
}
// 4.2 检查余额是否充足
String checkBalanceSQL = String.format(
"SELECT balance FROM accounts WHERE account_name = '%s'",
fromAccount
);
ResultSet rs = stmt.executeQuery(checkBalanceSQL);
if (rs.next()) {
double balance = rs.getDouble("balance");
if (balance < 0) {
throw new RuntimeException("余额不足,转账失败");
}
}
// 4.3 入账操作
String creditSQL = String.format(
"UPDATE accounts SET balance = balance + %.2f WHERE account_name = '%s'",
amount, toAccount
);
int creditResult = stmt.executeUpdate(creditSQL);
// 检查入账是否成功
if (creditResult == 0) {
throw new RuntimeException("转入账户不存在");
}
// 5. 所有操作成功,提交事务
conn.commit();
System.out.println("转账成功!");
} catch (Exception e) {
// 6. 出现异常,回滚事务
try {
if (conn != null) {
conn.rollback();
System.out.println("转账失败,事务已回滚!");
}
} catch (SQLException rollbackEx) {
rollbackEx.printStackTrace();
}
System.err.println("错误信息: " + e.getMessage());
} finally {
// 7. 释放资源
try {
if (stmt != null) stmt.close();
if (conn != null) {
conn.setAutoCommit(true); // 恢复自动提交
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
方案2:优化版 - 带事务边界控制
import java.sql.*;
public class TransactionDemo2 {
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/bank_db";
private static final String USER = "root";
private static final String PASSWORD = "your_password";
public static void main(String[] args) {
// 测试用例
testTransactionSuccess(); // 测试成功场景
testTransactionFailure(); // 测试失败场景
testBalanceInsufficient(); // 测试余额不足场景
}
/**
* 转账服务类
*/
static class TransferService {
/**
* 执行转账(方法级事务控制)
*/
public void transfer(String fromAccount, String toAccount, double amount)
throws SQLException {
// 获取连接
try (Connection conn = getConnection()) {
// 开启事务
TransactionManager.beginTransaction(conn);
try {
// 执行转账逻辑
performTransfer(conn, fromAccount, toAccount, amount);
// 提交事务
TransactionManager.commit(conn);
System.out.println("转账成功:" + fromAccount + " -> " + toAccount + " 金额:" + amount);
} catch (Exception e) {
// 回滚事务
TransactionManager.rollback(conn);
System.err.println("转账失败,已回滚:" + e.getMessage());
throw new RuntimeException("转账失败", e);
}
}
}
/**
* 实际转账操作
*/
private void performTransfer(Connection conn, String fromAccount,
String toAccount, double amount) throws SQLException {
String sql = null;
try (PreparedStatement pstmt = conn.prepareStatement(
"UPDATE accounts SET balance = balance + ? WHERE account_name = ?")) {
// 1. 更新转出账户余额(扣款)
pstmt.setDouble(1, -amount);
pstmt.setString(2, fromAccount);
int row1 = pstmt.executeUpdate();
if (row1 != 1) {
throw new SQLException("转出账户不存在");
}
// 2. 检查转出账户余额是否足够
if (!isBalanceEnough(conn, fromAccount, amount)) {
throw new SQLException("余额不足");
}
// 3. 更新转入账户余额(入账)- 重新设置参数
pstmt.setDouble(1, amount);
pstmt.setString(2, toAccount);
int row2 = pstmt.executeUpdate();
if (row2 != 1) {
throw new SQLException("转入账户不存在");
}
}
}
/**
* 检查账户余额是否足够
*/
private boolean isBalanceEnough(Connection conn, String accountName,
double amount) throws SQLException {
String sql = "SELECT balance FROM accounts WHERE account_name = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, accountName);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
return rs.getDouble("balance") >= amount;
}
return false;
}
}
}
}
/**
* 事务管理器 - 专门负责事务控制
*/
static class TransactionManager {
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
public static void beginTransaction(Connection conn) throws SQLException {
conn.setAutoCommit(false);
}
public static void commit(Connection conn) throws SQLException {
conn.commit();
conn.setAutoCommit(true);
}
public static void rollback(Connection conn) throws SQLException {
conn.rollback();
conn.setAutoCommit(true);
}
}
/**
* 测试方法
*/
private static void testTransactionSuccess() {
System.out.println("=== 测试1:正常转账 ===");
TransferService service = new TransferService();
try {
service.transfer("张三", "李四", 500.00);
System.out.println("测试通过!");
} catch (Exception e) {
System.out.println("测试失败!");
}
}
private static void testTransactionFailure() {
System.out.println("\n=== 测试2:账户不存在 ===");
TransferService service = new TransferService();
try {
service.transfer("张三", "王五", 500.00); // 王五不存在
System.out.println("测试失败!");
} catch (Exception e) {
System.out.println("测试通过! 错误:" + e.getMessage());
// 验证数据一致性
checkDataConsistency();
}
}
private static void testBalanceInsufficient() {
System.out.println("\n=== 测试3:余额不足 ===");
TransferService service = new TransferService();
try {
service.transfer("张三", "李四", 50000.00); // 余额不足
System.out.println("测试失败!");
} catch (Exception e) {
System.out.println("测试通过! 错误:" + e.getMessage());
checkDataConsistency();
}
}
/**
* 检查数据一致性
*/
private static void checkDataConsistency() {
try (Connection conn = TransactionManager.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT account_name, balance FROM accounts")) {
System.out.println("当前账户余额:");
while (rs.next()) {
System.out.println(" " + rs.getString("account_name") +
":" + rs.getDouble("balance"));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
方案3:最佳实践 - 通用事务模板
import java.sql.*;
/**
* 通用JDBC事务处理模板
*/
public abstract class JdbcTemplate {
private static final String URL = "jdbc:mysql://localhost:3306/bank_db";
private static final String USER = "root";
private static final String PASSWORD = "your_password";
/**
* 在事务中执行操作
*/
public void executeInTransaction() throws SQLException {
Connection conn = null;
Savepoint savepoint = null;
try {
// 创建一个新连接
conn = DTBase.getConnection();
// 开启事务
conn.setAutoCommit(false);
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
// 设置保存点(可选)
savepoint = conn.setSavepoint("start");
// 执行具体操作
doInTransaction(conn);
// 提交事务
conn.commit();
} catch (SQLException e) {
if (conn != null) {
try {
// 回滚到保存点或回滚整个事务
if (savepoint != null) {
conn.rollback(savepoint);
} else {
conn.rollback();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
}
throw e;
} finally {
if (conn != null) {
try {
conn.setAutoCommit(true);
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/**
* 抽象方法,由子类实现具体的业务逻辑
*/
protected abstract void doInTransaction(Connection conn) throws SQLException;
/**
* 数据库连接工厂类
*/
private static class DTBase {
public static Connection getConnection() throws SQLException {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
return DriverManager.getConnection(URL, USER, PASSWORD);
} catch (ClassNotFoundException e) {
throw new SQLException("数据库驱动加载失败", e);
}
}
}
}
/**
* 具体业务实现
*/
class TransferTask extends JdbcTemplate {
private String fromAccount;
private String toAccount;
private double amount;
public TransferTask(String from, String to, double amount) {
this.fromAccount = from;
this.toAccount = to;
this.amount = amount;
}
@Override
protected void doInTransaction(Connection conn) throws SQLException {
// 业务逻辑实现
String sql = "UPDATE accounts SET balance = balance + ? WHERE account_name = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 扣款
pstmt.setDouble(1, -amount);
pstmt.setString(2, fromAccount);
int count = pstmt.executeUpdate();
if (count == 0) {
throw new SQLException("转出账户不存在");
}
// 入账
pstmt.setDouble(1, amount);
pstmt.setString(2, toAccount);
count = pstmt.executeUpdate();
if (count == 0) {
throw new SQLException("转入账户不存在");
}
}
}
}
事务管理核心要点
事务特性(ACID)
- 原子性:事务中的所有操作要么全部成功,要么全部失败
- 一致性:事务执行前后,数据库完整性约束保持不变
- 隔离性:并发事务之间互不干扰
- 持久性:事务提交后,对数据库的改变是永久的
关键代码说明
// 开启事务
conn.setAutoCommit(false);
// 设置保存点
Savepoint sp = conn.setSavepoint("savepointName");
// 回滚到保存点
conn.rollback(sp);
// 提交事务
conn.commit();
// 回滚事务
conn.rollback();
事务隔离级别
// 设置隔离级别 conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
运行结果示例
=== 测试1:正常转账 ===
转账成功:张三 -> 李四 金额:500.0
测试通过!
=== 测试2:账户不存在 ===
转账失败,已回滚:转出账户不存在
测试通过! 当前账户余额:
张三:4830.0
李四:5170.0
=== 测试3:余额不足 ===
转账失败,已回滚:余额不足
测试通过! 当前账户余额:
张三:4830.0
李四:5170.0
注意事项
- 连接管理:使用try-with-resources自动管理资源
- 事务边界:明确事务的开始和结束位置
- 异常处理:捕获所有异常,确保事务正确回滚
- 性能考虑:事务尽量短,避免长时间占用连接
- 并发控制:合理设置事务隔离级别
- 日志记录:记录关键操作和异常信息
这个案例完整展示了JDBC事务管理的核心功能和最佳实践,可以根据实际需求选择合适的实现方案。