事務是一個完整的操作。事務的各步操作是不可分的(原子的);要麼都執行,要麼都不執行。
-- 創建表 create table account_money ( id number(4) not null, name varchar2(4) not null, money number(5,2) not null ) ; -- 增加一個檢查約束 alter table account_money add constraint CK_money check (money>=0);
--向張三這個賬號增加數據 insert into ACCOUNT_MONEY (ID, NAME, MONEY) values (1001, '張三', 500.00); insert into ACCOUNT_MONEY (ID, NAME, MONEY) values (1002, '張三', 1.00);
增加後的表如下:
ID NAME MONEY
1 1001 張三 500.00
2 1002 張三 1.00
以下為oracle事務處理
BEGIN --從張三的1001賬戶轉入張三的1002賬戶 UPDATE account_money a SET a.Money=a.Money-600 WHERE a.Id='1001'; UPDATE account_money a SET a.Money=a.Money+600 WHERE a.Id='1002'; COMMIT;--提交事務 EXCEPTION --異常處理 WHEN OTHERS THEN ROLLBACK;--出現異常就回滾 Dbms_Output.Put_Line('轉賬異常,轉賬失敗');
在上述代碼中,因為賬戶設置了檢查約束,當賬戶小於0時,就會出現異常,如果不進行事務異常處理,那麼第二條更新語句會被執行。當做了事務異常處理後,當出現異常就會回滾。
在事務操作前後,數據必須處於一致狀態。是一個業務規則約束的范疇。
同樣使用以上的表來做以說明:
DECLARE account_a account_money.Money%TYPE; account_b account_money.Money%TYPE; BEGIN SELECT a.money INTO account_a FROM account_money a WHERE a.Id='1001'; SELECT a.money INTO account_b FROM account_money a WHERE a.Id='1002'; Dbms_Output.Put_Line('轉賬前A賬戶余額:'||account_a); Dbms_Output.Put_Line('轉賬前B賬戶余額:'||account_b); Dbms_Output.Put_Line('轉賬前總余額:'||(account_a+account_b)); UPDATE account_money SET money=money-100 WHERE ID='1001'; UPDATE account_money SET money=money+100 WHERE ID='1002'; COMMIT; SELECT a.money INTO account_a FROM account_money a WHERE a.Id='1001'; SELECT a.money INTO account_b FROM account_money a WHERE a.Id='1002'; Dbms_Output.Put_Line('轉賬後A賬戶余額:'||account_a); Dbms_Output.Put_Line('轉賬後B賬戶余額:'||account_b); Dbms_Output.Put_Line('轉賬後總余額:'||(account_a+account_b)); EXCEPTION WHEN OTHERS THEN Dbms_Output.Put_Line('轉賬失敗,業務取消'); SELECT a.money INTO account_a FROM account_money a WHERE a.Id='1001'; SELECT a.money INTO account_b FROM account_money a WHERE a.Id='1002'; Dbms_Output.Put_Line('停止轉賬後A賬戶余額:'||account_a); Dbms_Output.Put_Line('停止轉賬後B賬戶余額:'||account_b); Dbms_Output.Put_Line('停止轉賬後總余額:'||(account_a+account_b)); END;
執行上段代碼,
執行第一遍:
轉賬前A賬戶余額:500
轉賬前B賬戶余額:1
轉賬前總余額:501
轉賬後A賬戶余額:400
轉賬後B賬戶余額:101
轉賬後總余額:501
執行第二遍:
轉賬前A賬戶余額:400
轉賬前B賬戶余額:101
轉賬前總余額:501
轉賬後A賬戶余額:300
轉賬後B賬戶余額:201
轉賬後總余額:501
執行第三遍:
轉賬前A賬戶余額:300
轉賬前B賬戶余額:201
轉賬前總余額:501
轉賬後A賬戶余額:200
轉賬後B賬戶余額:301
轉賬後總余額:501
。。。。。。
當執行第5遍時:A賬戶的余額為0,如果再執行會出現象呢?
轉賬前A賬戶余額:100
轉賬前B賬戶余額:401
轉賬前總余額:501
轉賬後A賬戶余額:0
轉賬後B賬戶余額:501
轉賬後總余額:501
執行第6遍,第7遍…………:
轉賬前A賬戶余額:0
轉賬前B賬戶余額:501
轉賬前總余額:501
轉賬失敗,業務取消
停止轉賬後A賬戶余額:0
停止轉賬後B賬戶余額:501
停止轉賬後總余額:501
我們會發現,當我們做事務處理後,總額不會發生變化,當出現異常就不會再執行(或者說回滾)!
對數據進行修改的所有並發事務是彼此隔離的,這表明事務必須是獨立的,它不是以任何方式依賴於或影響其它事務。
每個事務是獨立的,我們在PL/SQL中新建兩個SQL窗口就以可以做兩個事務處理。
我們還是使用以上表,表內容如下:
ID NAME MONEY
1 1001 張三 0.00
2 1002 張三 501.00
第一個SQL窗口:
第二個SQL窗口:
輸入:SELECT * FROM account_money;這條查詢語句,我們會發現,第一個SQL窗口沒有執行之前的數據。
如果我們也在第二個SQL窗口使用update更新數據會怎麼樣呢?
UPDATE account_money SET money=money+300 WHERE ID='1001';
SELECT * FROM account_money;
他會等待第一個SQL窗口提交事務才會有更新結果!
這時我們提交第一個SQL窗口的事務,我們會看到,兩個窗口的結果都發生變化。
我們再提交第二個SQL窗口的事務(在第一個SQL窗口事務沒有提交之前是不能提交第二個窗口的事務的),結果也同上圖!
事務完成後,它對數據庫的修改被永久保存下來。