事務是一個完整的操做。事務的各步操做是不可分的(原子的);要麼都執行,要麼都不執行。sql
-- 建立表 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事務處理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時,就會出現異常,若是不進行事務異常處理,那麼第二條更新語句會被執行。當作了事務異常處理後,當出現異常就會回滾。3d
在事務操做先後,數據必須處於一致狀態。是一個業務規則約束的範疇。blog
一樣使用以上的表來作以說明:事務
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;
執行上段代碼,ci
執行第一遍:get
轉帳前A帳戶餘額:500
轉帳前B帳戶餘額:1
轉帳前總餘額:501
轉帳後A帳戶餘額:400
轉帳後B帳戶餘額:101
轉帳後總餘額:501
it
執行第二遍:
轉帳前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窗口事務沒有提交以前是不能提交第二個窗口的事務的),結果也同上圖!
事務完成後,它對數據庫的修改被永久保存下來。