事務是數據庫操作的邏輯工作單元,要麼全做,要麼全不做。JDBC 作為 Java 訪問數據庫的基石,提供了完整的事務控制 API。本文將系統講解事務 ACID 特性、JDBC 事務 API、隔離級別與併發異常、保存點機制,並給出生產級代碼範本與最佳實踐。
| 特性 | 英文 | 核心含義 |
|---|---|---|
| 原子性 | Atomicity | 事務是不可分割的最小單元,所有操作全成或全敗。 |
| 一致性 | Consistency | 事務執行前後,數據庫從一個一致性狀態變為另一個一致性狀態 (完整性約束不被破壞)。 |
| 隔離性 | Isolation | 並發事務互不干擾,中間狀態對其他事務不可見。 |
| 持久性 | Durability | 事務提交後,修改永久保存,即使系統崩潰也不丟失 (寫入 Redo Log/數據文件)。 |
java.sql.Connection)// 1. 關閉自動提交 (開啟事務) — **關鍵第一步**
conn.setAutoCommit(false); // 默認 true:每條 SQL 獨立提交
try {
// 2. 業務 SQL 操作 (DML: insert/update/delete)
PreparedStatement ps1 = conn.prepareStatement("UPDATE account SET balance=balance-100 WHERE id=1");
ps1.executeUpdate();
PreparedStatement ps2 = conn.prepareStatement("UPDATE account SET balance=balance+100 WHERE id=2");
ps2.executeUpdate();
// 3. 顯式提交事務
conn.commit();
conn.setAutoCommit(true); // 恢復自動提交模式 (可選,視連接池策略)
} catch (Exception e) {
// 4. 異常回滾
try { conn.rollback(); } catch (SQLException ignored) {}
throw new RuntimeException("事務失敗", e);
} finally {
// 5. 資源關閉 (建議用 try-with-resources 或工具類)
close(ps1, ps2, conn);
}
autoCommit=true 時,每條 SQL 隱式提交,**無法構成多條 SQL 的原子操作**。**必須設置 false 開啟事務**。CREATE/DROP/ALTER (DDL)、GRANT/REVOKE (DCL) 會**強制提交**當前事務,不可回滾。事務中避免混用。rollback()**,否則事務懸掛持有鎖,引發死鎖/阻塞。JDBC 定義 5 級隔離 (Connection.TRANSACTION_*):
| 級別常量 | 隔離級別 | 髒讀 | 不可重複讀 | 幻讀 | 性能/並發 |
|---|---|---|---|---|---|
TRANSACTION_NONE | 不支持事務 | - | - | - | - |
TRANSACTION_READ_UNCOMMITTED | 讀未提交 | ✅ 允許 | ✅ 允許 | ✅ 允許 | 最高/最低 |
TRANSACTION_READ_COMMITTED | 讀已提交 (Oracle/MySQL 默認) | ❌ 禁止 | ✅ 允許 | ✅ 允許 | 高/高 |
TRANSACTION_REPEATABLE_READ | 可重複讀 (MySQL InnoDB 默認) | ❌ 禁止 | ❌ 禁止 | ✅ 允許 | 中/中 |
TRANSACTION_SERIALIZABLE | 串行化 | ❌ 禁止 | ❌ 禁止 | ❌ 禁止 | 最低/最低 |
// 必須在 setAutoCommit(false) 之前設置,或新連接上設置
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
// 獲取當前級別
int level = conn.getTransactionIsolation();
建議:大多數業務用默認級別 (Read Committed / Repeatable Read)。極高一致性需求 (如財務對賬) 可升級 Serializable,但需評估性能代價。避免使用 Read Uncommitted。
允許在事務中設置標記,回滾到指定點而非整個事務。
Savepoint sp1 = conn.setSavepoint("SP1"); // 命名保存點
// ... 執行一些 SQL ...
Savepoint sp2 = conn.setSavepoint(); // 匿名保存點
// ... 執行風險操作 ...
if (riskCheckFailed) {
conn.rollback(sp1); // 回滾到 SP1,SP1 後操作撤銷,SP1 前保留
}
// 繼續其他操作...
conn.commit(); // 最終提交
conn.releaseSavepoint(sp1) (可選,釋放資源)。public class JdbcTxTemplate {
// 執行帶事務的操作,自動處理提交/回滾/資源關閉
public static T execute(Connection conn, TxCallback callback) {
boolean originalAutoCommit = true;
try {
originalAutoCommit = conn.getAutoCommit();
conn.setAutoCommit(false); // 開啟事務
T result = callback.doInTransaction(conn); // 業務邏輯
conn.commit(); // 提交
return result;
} catch (Exception e) {
try { conn.rollback(); } catch (SQLException ignored) {}
throw new RuntimeException("事務執行失敗", e);
} finally {
try { conn.setAutoCommit(originalAutoCommit); } catch (SQLException ignored) {} // 恢復
}
}
@FunctionalInterface
public interface TxCallback {
T doInTransaction(Connection conn) throws Exception;
}
}
// 使用示例
public void transfer(long fromId, long toId, double amount) {
JdbcTxTemplate.execute(dataSource.getConnection(), conn -> {
updateBalance(conn, fromId, -amount);
updateBalance(conn, toId, amount);
return null;
});
}
實際項目中**極少手寫 JDBC 事務代碼**,均使用 Spring @Transactional:
@Service
@RequiredArgsConstructor
public class AccountService {
private final JdbcTemplate jdbcTemplate; // 或 DataSource
@Transactional(rollbackFor = Exception.class) // 核心註解
public void transfer(long from, long to, double amount) {
jdbcTemplate.update("UPDATE account SET balance=balance-? WHERE id=?", amount, from);
jdbcTemplate.update("UPDATE account SET balance=balance+? WHERE id=?", amount, to);
// 異常自動回滾,正常自動提交,資源自動管理
}
}
Spring 事務優勢:聲明式、AOP 無侵入、傳播行為 (REQUIRED/REQUIRES_NEW 等)、隔離級別、只讀優化、超時控制、與 ORM (JPA/MyBatis) 無縫整合。
setAutoCommit(false)** 開啟事務,執行完 commit()。rollback()**,防止鎖懸掛。掌握 JDBC 事務原理,是理解 Spring 事務、排查死鎖/數據不一致、設計高併發數據層的基石。以「短小精悍、異常必滾、框架托管」為原則,讓事務成為數據一致性的可靠守護者。