1.銀行轉(zhuǎn)賬案例
案例:銀行轉(zhuǎn)賬:從張無忌賬戶上給趙敏轉(zhuǎn)1000塊錢.
準(zhǔn)備:account(賬戶表):
id name(賬號,唯一) balance(余額)
1 張無忌 20000
2 趙敏 0
轉(zhuǎn)賬操作步驟:
①:查詢張無忌的賬戶余額是否大于等于1000.
SELECT * FROM account WHERE name = '王健林' AND balance >= 100;
余額小于1000 : 溫馨提示:親,你的余額不足.
余額大于等于1000: GOTO 2;
②:從王健林的賬戶余額中減少100.
UPDATE account SET balance = balance - 1000 WHERE name = '王健林';
③:在馬云的賬戶余額中增加100.
UPDATE account SET balance = balance + 1000 WHERE name = '馬云';
public class TransactionTest extends TestCase {
// 案例:銀行轉(zhuǎn)賬:從王健林賬戶上轉(zhuǎn)100個億給馬云
public void testTranscation() throws Exception {
// 1.查詢王健林賬戶的余額是否大約100億
String sql = "SELECT * FROM t_account WHERE name =? AND balance >= ?";
Connection conn = JdbcUtil.getConn();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "王健林");
ps.setInt(2, 2000);
ResultSet rs = ps.executeQuery();
int wBalance = 0;
if (rs.next()) {
wBalance = rs.getInt("balance");
System.out.println(rs.getString("name") + "還有" + wBalance + "的余額");
} else {
throw new RuntimeException("余額不足");
}
// 2.從王健林的賬戶中余額減少100
sql = "UPDATE t_account SET balance= ? WHERE name= ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, wBalance - 100);
ps.setString(2, "王健林");
ps.executeUpdate();
// 3.查詢馬云的余額
int mBalance = 0;
sql = "SELECT * FROM t_account WHERE name =?";
ps = conn.prepareStatement(sql);
ps.setString(1, "馬云");
rs = ps.executeQuery();
if (rs.next()) {
mBalance = rs.getInt("balance");
System.out.println(rs.getString("name") + "還有" + mBalance + "的余額");
} else {
throw new RuntimeException("沒有馬云賬戶");
}
// 4.給馬云的賬戶余額中增加100
sql = "UPDATE t_account SET balance= ? WHERE name= ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, mBalance + 100);
ps.setString(2, "馬云");
ps.executeUpdate();
// 關(guān)閉資源
JdbcUtil.close(conn, ps, rs);
}
}
悲劇的事情來了:當(dāng)程序執(zhí)行到第②步和第③步之間,突然停電了.
使用異常來模擬停電:System.out.println(1/0);
2.JDBC的事務(wù)操作
事務(wù)(Transaction,簡寫為tx):
在數(shù)據(jù)庫中,所謂事務(wù)是指一組邏輯操作單元,使數(shù)據(jù)從一種狀態(tài)變換到另一種狀態(tài)。
為確保數(shù)據(jù)庫中數(shù)據(jù)的一致性,數(shù)據(jù)的操縱應(yīng)當(dāng)是離散的成組的邏輯單元:
(1)當(dāng)每個邏輯操作單元全部完成時,數(shù)據(jù)的一致性可以保持;
(2)而當(dāng)這個單元中的一部分操作失敗,整個事務(wù)應(yīng)全部視為錯誤,所有從起始點以后的操作應(yīng)全部回退到開始狀態(tài)。事務(wù)的操作:先定義開始一個事務(wù),然后對數(shù)據(jù)作修改操作,這時如果提交(commit),這些修改就永久地保存下來,如果回退(rollback),數(shù)據(jù)庫管理系統(tǒng)將放棄您所作的所有修改而回到開始事務(wù)時的狀態(tài)。
事務(wù):指構(gòu)成單個邏輯工作單元的操作集合。
事務(wù)處理:保證所有事務(wù)都作為一個工作單元來執(zhí)行,即使出現(xiàn)了故障,都不能改變這種執(zhí)行方式。當(dāng)在一個事務(wù)中執(zhí)行多個操作時,要么所有的事務(wù)都被提交(commit),要么整個事務(wù)回滾(rollback)到最初狀態(tài)。
簡單講:事務(wù)其實就是多個操作,把多個操作看成是一個不可分割的整體,整體中的多個要成功都成功,要失敗都失敗。
在紅樓夢中: 一損俱損,就是這個思想.
事務(wù)的ACID屬性:
1. 原子性(Atomicity):原子在化學(xué)中,是最小單位,不可以再分割了。
原子性是指事務(wù)是一個不可分割的工作單位,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生。
2. 一致性(Consistency):包裝數(shù)據(jù)的完整性。
事務(wù)必須使數(shù)據(jù)庫從一個一致性狀態(tài)變換到另外一個一致性狀態(tài)。(數(shù)據(jù)不被破壞)
3. 隔離性(Isolation):事務(wù)的隔離性是指一個事務(wù)的執(zhí)行不能被其他事務(wù)干擾,即一個事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對并發(fā)的其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個事務(wù)之間不能互相干擾。
4. 持久性(Durability):
持久性是指一個事務(wù)一旦被提交,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就是永久性的,接下來的其他操作和數(shù)據(jù)庫故障不應(yīng)該對其有任何影響。
事務(wù)的操作:
事務(wù)成功: 提交事務(wù)(commit),如果事務(wù)不提交,在數(shù)據(jù)庫中數(shù)據(jù)永遠都不會改變;
事務(wù)失?。?/strong> 出現(xiàn)異常的時候,事務(wù)失敗.事務(wù)回滾:rollback(取消之前所有的操作,回到事務(wù)最初的狀態(tài)),釋放鎖資源。
操作事務(wù)的模板:
try{
取消事務(wù)的自動提交機制,設(shè)置為手動提交. connection對象.setAutoCommit(false);
操作1
操作2
異常
操作3
....
手動提交事務(wù) connection對象.commit();
}catch(Exception e){
//處理異常
回滾事務(wù) connection對象.rollback();
}
上面案例的事務(wù)處理
public void testTranscation() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConn();
conn.setAutoCommit(false);// 取消事務(wù)的自動提交機制
String sql = "SELECT * FROM t_account WHERE name =? AND balance >= ?";
ps = conn.prepareStatement(sql);
ps.setString(1, "王健林");
ps.setInt(2, 2000);
rs = ps.executeQuery();
int wBalance = 0;
if (rs.next()) {
wBalance = rs.getInt("balance");
System.out.println(rs.getString("name") + "還有" + wBalance + "的余額");
} else {
throw new RuntimeException("余額不足");
}
// 2.從王健林的賬戶中余額減少100
sql = "UPDATE t_account SET balance= ? WHERE name= ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, wBalance - 100);
ps.setString(2, "王健林");
ps.executeUpdate();
// 3.查詢馬云的余額
int mBalance = 0;
sql = "SELECT * FROM t_account WHERE name =?";
ps = conn.prepareStatement(sql);
ps.setString(1, "馬云");
rs = ps.executeQuery();
if (rs.next()) {
mBalance = rs.getInt("balance");
System.out.println(rs.getString("name") + "還有" + mBalance + "的余額");
} else {
throw new RuntimeException("沒有馬云賬戶");
}
System.out.println(1 / 0); // 模擬停電
// 4.給馬云的賬戶余額中增加100
sql = "UPDATE t_account SET balance= ? WHERE name= ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, mBalance + 100);
ps.setString(2, "馬云");
ps.executeUpdate();
conn.commit();//提交事務(wù)
System.out.println("轉(zhuǎn)賬成功");
} catch (Exception e) {
try {
conn.rollback(); //事務(wù)回滾
System.out.println("轉(zhuǎn)賬失敗");
} catch (Exception e2) {
e2.printStackTrace();
}
} finally {
// 關(guān)閉資源
JdbcUtil.close(conn, ps, rs);
}
}
事務(wù)相關(guān)的:
- 1.默認(rèn)情況下,事務(wù)在執(zhí)行完DML操作就自動提交.
- 2.查詢操作,其實是不需要事務(wù)的.但是,一般的,我們在開發(fā)中都把查詢放入事務(wù)中.
- 3.開發(fā)中,代碼完全正確,沒有異常,但是就是數(shù)據(jù)庫中數(shù)據(jù)不變.
意識:沒有提交事務(wù). - 4.在MySQL中,只有InnoDB存儲引擎支持事務(wù),支持外鍵,MyISAM不支持事務(wù).
- 5.以后事務(wù)我們不應(yīng)該在DAO層處理,應(yīng)該在service層控制.
- 6.事務(wù)在講解Hibernate,MyBatis,Spring,項目的時候都會再講.