事務是資料庫一致性的基石。本文系統講解事務核心概念、ACID 四大特性、SQL 控制語句、隔離級別與鎖關係,並提供 Java JDBC 事務模板代碼,對比一般語句、批處理、事務處理三者差異。
定義:一組操作組成的邏輯工作單元,要麼全做,要麼全不做。
經典場景:銀行轉賬 —— 扣款與增款必須同時成功或同時失敗。
| 操作 | SQL 語句 | 說明 |
|---|---|---|
| 開始事務 | BEGIN / START TRANSACTION | 標誌事務起點 |
| 提交事務 | COMMIT | 持久化所有變更 |
| 回滾事務 | ROLLBACK | 撤銷所有變更 |
| 保存點 | SAVEPOINT name / ROLLBACK TO name | 部分回滾 |
| 隔離級別 | 髒讀 | 不可重複讀 | 幻讀 | MySQL 默認 |
|---|---|---|---|---|
| READ UNCOMMITTED | ✅ | ✅ | ✅ | |
| READ COMMITTED | ❌ | ✅ | ✅ | |
| REPEATABLE READ | ❌ | ❌ | ✅ (間隙鎖緩解) | ✅ InnoDB 默認 |
| SERIALIZABLE | ❌ | ❌ | ❌ |
SELECT @@tx_isolation; SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;@Test
public void save1() throws Exception {
Connection con = ConnFactory.getConn();
Statement st = con.createStatement();
st.execute("INSERT INTO stud(id,sname,age) VALUES(1010,'益陽1',25)");
st.execute("INSERT INTO stud(id,sname,age) VALUES(1010,'益陽2',28)"); // 主鍵衝突拋異常
st.execute("INSERT INTO stud(id,sname,age) VALUES(1011,'益陽3',25)"); // 不會執行
}
特點:逐條提交,異常中斷後續執行,前面已提交無法回滾。
@Test
public void save2() throws Exception {
Connection con = ConnFactory.getConn();
Statement st = con.createStatement();
st.addBatch("INSERT INTO stud(id,sname,age) VALUES(1010,'益陽1',25)");
st.addBatch("INSERT INTO stud(id,sname,age) VALUES(1010,'益陽2',28)"); // 失敗
st.addBatch("INSERT INTO stud(id,sname,age) VALUES(1011,'益陽3',25)"); // 繼續執行
st.executeBatch(); // 返回每條影響行數數組
}
特點:一次網絡往返發送多條,合法語句執行,失敗單條不影響其它,**無事務保護**。
@Test
public void save3() throws Exception {
Connection con = ConnFactory.getConn();
try {
con.setAutoCommit(false); // 關鍵:開啟事務
Statement st = con.createStatement();
st.execute("INSERT INTO stud(id,sname,age) VALUES(1010,'益陽1',25)");
st.execute("INSERT INTO stud(id,sname,age) VALUES(1010,'益陽2',28)"); // 失敗
st.execute("INSERT INTO stud(id,sname,age) VALUES(1012,'益陽3',25)");
con.commit(); // 全部成功才提交
} catch (Exception e) {
System.out.println("回滾了....");
con.rollback(); // 任一失敗全量回滾
} finally {
con.setAutoCommit(true); // 恢復默認
con.close();
}
}
特點:原子性保障,異常自動回滾,資源 finally 釋放。
// jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/hncu?useUnicode=true&characterEncoding=UTF-8
username=root
password=1234
// ConnFactory.java
public class ConnFactory {
private static Connection conn;
static {
Properties p = new Properties();
p.load(ConnFactory.class.getClassLoader().getResourceAsStream("jdbc.properties"));
Class.forName(p.getProperty("driver"));
conn = DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"), p.getProperty("password"));
}
public static Connection getConn() { return conn; }
}
事務不僅是語法,更是業務一致性的契約。掌握模板,落地規範,讓數據永遠可信。