數據庫事務介紹
事務:一組邏輯操作單元,使數據 從一種狀態(tài)變換到另一種狀態(tài)。
- 一組邏輯操作單元:一個或多個DML操作(增刪改)
事務處理原則:保證所有事務都作為一個工作單元來執(zhí)行,即使出現了故障,都不能改變這種執(zhí)行方式。當一個事務中執(zhí)行多個操作時,要么所有的事務都被提交(commit),那么這些修改就永久地保存下來,要么數據庫管理系統將放棄所做的所有修改,整個事務回滾(rollback)到最初狀態(tài)。
JDBC事務處理
數據一旦提交,就不可回滾。
哪些操作會導致數據的自動提交?
- DDL操作一旦執(zhí)行,都會自動提交。
set autocomit = false對DDL操作無效。 - DML操作默認情況下,一旦執(zhí)行,就會自動提交,我們可以通過
set autocomit = false的方式取消DML操作的自動提交。 - 默認在關閉連接時,會自動地提交數據。
以上的三種情況,要都避免。
在編寫代碼過程中,可以取消自動提交,以及控制何時關閉連接??梢酝ㄟ^下面的三個方法讓一個或多個DML操作作為一個事務執(zhí)行:
setAutoCommit(false):Connection對象的方法,取消自動提交事務。
commit():在所有的SQL語句都成功執(zhí)行后,調用該方法提交事務。
rollback():在出現異常時,調用該方法回滾事務。
若此時Connection沒有被關閉,還可能被重復使用,尤其是在使用數據庫連接池技術時,執(zhí)行close()方法前,建議恢復自動提交狀態(tài)setAutoCommit(true)。
案例
用戶AA向用戶BB轉賬100:
@Test
public void testUpdate() {
Connection conn = null;
try {
// 1. 獲取數據庫連接,在此獲取連接,保證之后的DML操作在同一個連接下進行。
conn = JDBCUtils.getConnection();
// 2. 關閉自動提交
conn.setAutoCommit(false);
// 3. 進行數據庫操作
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(conn, sql1, "AA");
// 模擬網絡異常
System.out.println(10 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn, sql2, "BB");
// 若沒有異常,提交事務
conn.commit();
System.out.println("轉賬成功");
} catch (Exception e) {
e.printStackTrace();
// 5. 若有異常,則回滾事務
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
} finally {
try {
// 6. 恢復每次DML操作的自動提交功能
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
// 7. 關閉連接
JDBCUtils.closeResouse(conn, null);
}
}
//通用的增刪改操作 ---- version 2.0 考慮事務
public int update(Connection conn, String sql, Object ...args) {
PreparedStatement ps = null;
try {
// 1. 預編譯sql語句,返回PreparedStatement的實例
ps = conn.prepareStatement(sql);
// 2. 填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
// 3. 執(zhí)行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 4. 關閉資源
JDBCUtils.closeResouse(null, ps);
}
return 0;
}
事務的ACID屬性
原子性(
Atomicity)
原子性是指事務是一個不可分割的工作單位,事務中的操作要么都發(fā)生,要么都不發(fā)生。一致性(
Consistency)
事務必須使數據庫從一個一致性狀態(tài)變換到另外一個一致性狀態(tài)。隔離性(
Isolation)
事務的隔離性是指一個事務的執(zhí)行不能被其他事務干擾,即一個事務內部的操作及使用的數據對并發(fā)的其他事務是隔離的,并發(fā)執(zhí)行的各個事務之間不能互相干擾。持久性(
Durability)
持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來的其他操作和數據庫故障不應該對其有任何影響。
數據庫的并發(fā)問題
對于同時運行的多個事務, 當這些事務訪問數據庫中相同的數據時, 如果沒有采取必要的隔離機制, 就會導致各種并發(fā)問題:
臟讀: 對于兩個事務 T1, T2, T1 讀取了已經被 T2 更新但還沒有被提交的字段。之后, 若 T2 回滾, T1讀取的內容就是臨時且無效的。
不可重復讀: 對于兩個事務T1, T2, T1 讀取了一個字段, 然后 T2 更新了該字段。之后, T1再次讀取同一個字段, 值就不同了。
幻讀: 對于兩個事務T1, T2, T1 從一個表中讀取了一個字段, 然后 T2 在該表中插入了一些新的行。之后, 如果 T1 再次讀取同一個表, 就會多出幾行。
數據庫事務的隔離性: 數據庫系統必須具有隔離并發(fā)運行各個事務的能力, 使它們不會相互影響, 避免各種并發(fā)問題。
一個事務與其他事務隔離的程度稱為隔離級別。數據庫規(guī)定了多種事務隔離級別,不同隔離級別對應不同的干擾程度,隔離級別越高,數據一致性就越好, 但并發(fā)性越弱。
四種隔離級別
數據庫提供的4種事務隔離級別:
| 隔離級別 | 描述 |
|---|---|
| READ UNCOMMITTED(讀未提交) | 允許事務讀取為被其他事務提交的變更,臟讀、不可重復讀和幻讀問題仍然可能出現 |
| READ COMMITTED(讀已提交) | 只允許事務讀取已經被其他事務提交的變更,可以避免臟讀,但不可重復讀和幻讀問題仍然可能出現 |
| REPEATABLE READ(可重復讀) | 確保事務可以多次從一個字段中讀取相同的值,在這個事務持續(xù)期間,禁止其他事務對這個字段進行更新,可以避免臟讀和不可重復讀,但幻讀的問題仍然存在 |
| SERIALIZABLE(串行化) | 確保事務可以從一個表中讀取相同的行,在這個事務持續(xù)期間,禁止其他事務對該表執(zhí)行插入,更新和刪除操作,所有并發(fā)問題都可以避免,但性能十分低下 |
Oracle支持的 2 種事務隔離級別:READ COMMITED, SERIALIZABLE。 Oracle 默認的事務隔離級別為: READ COMMITED 。
Mysql支持 4 種事務隔離級別。Mysql 默認的事務隔離級別為: REPEATABLE READ。
在MySQL中設置隔離界別
每啟動一個 mysql 程序,就會獲得一個單獨的數據庫連接。每個數據庫連接都有一個全局變量 @@tx_isolation,表示當前的事務隔離級別。
-
查看當前的隔離級別:
SELECT @@tx_isolation; -
設置當前mysql連接的隔離級別:
set transaction isolation level read committed; -
設置數據庫系統的全局的隔離級別:
set global transaction isolation level read committed;
補充操作:
-
創(chuàng)建mysql數據庫用戶:
create user tom identified by '123456'; -
授予權限:
# 授予通過網絡方式登錄的tom用戶對所有庫所有表的全部權限,密碼設為123456 grant all privileges on *.* to tom@'%' identified by '123456'; # 給tom用戶使用本地命令行方式,授予test這個庫下的所有表的增刪改查的權限 grant select,insert,delete,update on test.* to tom@localhost identified by 123456;
在Java代碼中想要獲取和修改數據庫當前連接的隔離級別可以使用下面兩個方法:
getTransactionIsolation():使用Connection對象調用,獲取當前連接的隔離級別。
setTransactionIsolation():使用Connection對象調用,設置當前連接的隔離級別。