數(shù)據(jù)庫(kù)事務(wù)介紹
事務(wù):一組邏輯操作單元,使數(shù)據(jù) 從一種狀態(tài)變換到另一種狀態(tài)。
- 一組邏輯操作單元:一個(gè)或多個(gè)DML操作(增刪改)
事務(wù)處理原則:保證所有事務(wù)都作為一個(gè)工作單元來(lái)執(zhí)行,即使出現(xiàn)了故障聂受,都不能改變這種執(zhí)行方式扮饶。當(dāng)一個(gè)事務(wù)中執(zhí)行多個(gè)操作時(shí)逗鸣,要么所有的事務(wù)都被提交(commit
)枝嘶,那么這些修改就永久地保存下來(lái)帘饶,要么數(shù)據(jù)庫(kù)管理系統(tǒng)將放棄所做的所有修改,整個(gè)事務(wù)回滾(rollback
)到最初狀態(tài)群扶。
JDBC事務(wù)處理
數(shù)據(jù)一旦提交及刻,就不可回滾。
哪些操作會(huì)導(dǎo)致數(shù)據(jù)的自動(dòng)提交竞阐?
- DDL操作一旦執(zhí)行缴饭,都會(huì)自動(dòng)提交。
set autocomit = false
對(duì)DDL操作無(wú)效馁菜。 - DML操作默認(rèn)情況下茴扁,一旦執(zhí)行,就會(huì)自動(dòng)提交汪疮,我們可以通過(guò)
set autocomit = false
的方式取消DML操作的自動(dòng)提交峭火。 - 默認(rèn)在關(guān)閉連接時(shí),會(huì)自動(dòng)地提交數(shù)據(jù)智嚷。
以上的三種情況卖丸,要都避免。
在編寫(xiě)代碼過(guò)程中盏道,可以取消自動(dòng)提交稍浆,以及控制何時(shí)關(guān)閉連接〔轮觯可以通過(guò)下面的三個(gè)方法讓一個(gè)或多個(gè)DML操作作為一個(gè)事務(wù)執(zhí)行:
setAutoCommit(false)
:Connection對(duì)象的方法衅枫,取消自動(dòng)提交事務(wù)。
commit()
:在所有的SQL語(yǔ)句都成功執(zhí)行后朗伶,調(diào)用該方法提交事務(wù)弦撩。
rollback()
:在出現(xiàn)異常時(shí),調(diào)用該方法回滾事務(wù)论皆。
若此時(shí)Connection沒(méi)有被關(guān)閉益楼,還可能被重復(fù)使用,尤其是在使用數(shù)據(jù)庫(kù)連接池技術(shù)時(shí)点晴,執(zhí)行close()方法前感凤,建議恢復(fù)自動(dòng)提交狀態(tài)setAutoCommit(true)。
案例
用戶(hù)AA向用戶(hù)BB轉(zhuǎn)賬100:
@Test
public void testUpdate() {
Connection conn = null;
try {
// 1. 獲取數(shù)據(jù)庫(kù)連接粒督,在此獲取連接陪竿,保證之后的DML操作在同一個(gè)連接下進(jìn)行。
conn = JDBCUtils.getConnection();
// 2. 關(guān)閉自動(dòng)提交
conn.setAutoCommit(false);
// 3. 進(jìn)行數(shù)據(jù)庫(kù)操作
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(conn, sql1, "AA");
// 模擬網(wǎng)絡(luò)異常
System.out.println(10 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn, sql2, "BB");
// 若沒(méi)有異常屠橄,提交事務(wù)
conn.commit();
System.out.println("轉(zhuǎn)賬成功");
} catch (Exception e) {
e.printStackTrace();
// 5. 若有異常萨惑,則回滾事務(wù)
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
} finally {
try {
// 6. 恢復(fù)每次DML操作的自動(dòng)提交功能
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
// 7. 關(guān)閉連接
JDBCUtils.closeResouse(conn, null);
}
}
//通用的增刪改操作 ---- version 2.0 考慮事務(wù)
public int update(Connection conn, String sql, Object ...args) {
PreparedStatement ps = null;
try {
// 1. 預(yù)編譯sql語(yǔ)句捐康,返回PreparedStatement的實(shí)例
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. 關(guān)閉資源
JDBCUtils.closeResouse(null, ps);
}
return 0;
}
事務(wù)的ACID屬性
原子性(
Atomicity
)
原子性是指事務(wù)是一個(gè)不可分割的工作單位,事務(wù)中的操作要么都發(fā)生庸蔼,要么都不發(fā)生。一致性(
Consistency
)
事務(wù)必須使數(shù)據(jù)庫(kù)從一個(gè)一致性狀態(tài)變換到另外一個(gè)一致性狀態(tài)贮匕。隔離性(
Isolation
)
事務(wù)的隔離性是指一個(gè)事務(wù)的執(zhí)行不能被其他事務(wù)干擾姐仅,即一個(gè)事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對(duì)并發(fā)的其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個(gè)事務(wù)之間不能互相干擾刻盐。持久性(
Durability
)
持久性是指一個(gè)事務(wù)一旦被提交掏膏,它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變就是永久性的,接下來(lái)的其他操作和數(shù)據(jù)庫(kù)故障不應(yīng)該對(duì)其有任何影響敦锌。
數(shù)據(jù)庫(kù)的并發(fā)問(wèn)題
對(duì)于同時(shí)運(yùn)行的多個(gè)事務(wù), 當(dāng)這些事務(wù)訪問(wèn)數(shù)據(jù)庫(kù)中相同的數(shù)據(jù)時(shí), 如果沒(méi)有采取必要的隔離機(jī)制, 就會(huì)導(dǎo)致各種并發(fā)問(wèn)題:
臟讀: 對(duì)于兩個(gè)事務(wù) T1, T2, T1 讀取了已經(jīng)被 T2 更新但還沒(méi)有被提交的字段馒疹。之后, 若 T2 回滾, T1讀取的內(nèi)容就是臨時(shí)且無(wú)效的。
不可重復(fù)讀: 對(duì)于兩個(gè)事務(wù)T1, T2, T1 讀取了一個(gè)字段, 然后 T2 更新了該字段乙墙。之后, T1再次讀取同一個(gè)字段, 值就不同了颖变。
幻讀: 對(duì)于兩個(gè)事務(wù)T1, T2, T1 從一個(gè)表中讀取了一個(gè)字段, 然后 T2 在該表中插入了一些新的行。之后, 如果 T1 再次讀取同一個(gè)表, 就會(huì)多出幾行听想。
數(shù)據(jù)庫(kù)事務(wù)的隔離性: 數(shù)據(jù)庫(kù)系統(tǒng)必須具有隔離并發(fā)運(yùn)行各個(gè)事務(wù)的能力, 使它們不會(huì)相互影響, 避免各種并發(fā)問(wèn)題腥刹。
一個(gè)事務(wù)與其他事務(wù)隔離的程度稱(chēng)為隔離級(jí)別。數(shù)據(jù)庫(kù)規(guī)定了多種事務(wù)隔離級(jí)別汉买,不同隔離級(jí)別對(duì)應(yīng)不同的干擾程度衔峰,隔離級(jí)別越高,數(shù)據(jù)一致性就越好, 但并發(fā)性越弱蛙粘。
四種隔離級(jí)別
數(shù)據(jù)庫(kù)提供的4種事務(wù)隔離級(jí)別:
隔離級(jí)別 | 描述 |
---|---|
READ UNCOMMITTED(讀未提交) | 允許事務(wù)讀取為被其他事務(wù)提交的變更垫卤,臟讀、不可重復(fù)讀和幻讀問(wèn)題仍然可能出現(xiàn) |
READ COMMITTED(讀已提交) | 只允許事務(wù)讀取已經(jīng)被其他事務(wù)提交的變更出牧,可以避免臟讀穴肘,但不可重復(fù)讀和幻讀問(wèn)題仍然可能出現(xiàn) |
REPEATABLE READ(可重復(fù)讀) | 確保事務(wù)可以多次從一個(gè)字段中讀取相同的值,在這個(gè)事務(wù)持續(xù)期間崔列,禁止其他事務(wù)對(duì)這個(gè)字段進(jìn)行更新梢褐,可以避免臟讀和不可重復(fù)讀,但幻讀的問(wèn)題仍然存在 |
SERIALIZABLE(串行化) | 確保事務(wù)可以從一個(gè)表中讀取相同的行赵讯,在這個(gè)事務(wù)持續(xù)期間盈咳,禁止其他事務(wù)對(duì)該表執(zhí)行插入,更新和刪除操作边翼,所有并發(fā)問(wèn)題都可以避免鱼响,但性能十分低下 |
Oracle支持的 2 種事務(wù)隔離級(jí)別:READ COMMITED
, SERIALIZABLE
。 Oracle 默認(rèn)的事務(wù)隔離級(jí)別為: READ COMMITED
组底。
Mysql支持 4 種事務(wù)隔離級(jí)別丈积。Mysql 默認(rèn)的事務(wù)隔離級(jí)別為: REPEATABLE READ
筐骇。
在MySQL中設(shè)置隔離界別
每啟動(dòng)一個(gè) mysql 程序,就會(huì)獲得一個(gè)單獨(dú)的數(shù)據(jù)庫(kù)連接江滨。每個(gè)數(shù)據(jù)庫(kù)連接都有一個(gè)全局變量 @@tx_isolation
铛纬,表示當(dāng)前的事務(wù)隔離級(jí)別。
-
查看當(dāng)前的隔離級(jí)別:
SELECT @@tx_isolation;
-
設(shè)置當(dāng)前mysql連接的隔離級(jí)別:
set transaction isolation level read committed;
-
設(shè)置數(shù)據(jù)庫(kù)系統(tǒng)的全局的隔離級(jí)別:
set global transaction isolation level read committed;
補(bǔ)充操作:
-
創(chuàng)建mysql數(shù)據(jù)庫(kù)用戶(hù):
create user tom identified by '123456';
-
授予權(quán)限:
# 授予通過(guò)網(wǎng)絡(luò)方式登錄的tom用戶(hù)對(duì)所有庫(kù)所有表的全部權(quán)限唬滑,密碼設(shè)為123456 grant all privileges on *.* to tom@'%' identified by '123456'; # 給tom用戶(hù)使用本地命令行方式告唆,授予test這個(gè)庫(kù)下的所有表的增刪改查的權(quán)限 grant select,insert,delete,update on test.* to tom@localhost identified by 123456;
在Java代碼中想要獲取和修改數(shù)據(jù)庫(kù)當(dāng)前連接的隔離級(jí)別可以使用下面兩個(gè)方法:
getTransactionIsolation()
:使用Connection
對(duì)象調(diào)用,獲取當(dāng)前連接的隔離級(jí)別晶密。
setTransactionIsolation()
:使用Connection
對(duì)象調(diào)用擒悬,設(shè)置當(dāng)前連接的隔離級(jí)別。