AUTONOMOUS TRANSACTION(自治事務)的介紹(轉)

zhouwf0726發表於2019-06-06
AUTONOMOUS TRANSACTION(自治事務)的介紹
在基於低版本的ORACLE做一些專案的過程中,有時會遇到一些頭疼的問題.,比如想在執行當前一個由多個DML組成的transaction(事務)時,為每一步DML記錄一些資訊到跟蹤表中,由於事務的原子性,這些跟蹤資訊的提交將決定於主事務的commit或rollback. 這樣一來寫程式的難度就增大了, 程式設計師不得不把這些跟蹤資訊記錄到類似陣列的結構中,然後在主事務結束後把它們存入跟蹤表.哎,真是麻煩!
有沒有一個簡單的方法解決類似問題呢?
ORACLE8i的AUTONOMOUS TRANSACTION(自治事務,以下AT)是一個很好的回答。
AT 是由主事務(以下MT)呼叫但是獨立於它的事務。在AT被呼叫執行時,MT被掛起,在AT內部,一系列的DML可以被執行並且commit或rollback.
注意由於AT的獨立性,它的commit和rollback並不影響MT的執行效果。在AT執行結束後,主事務獲得控制權,又可以繼續執行了。
如何實現AT的定義呢?我們來看一下它的語法。其實非常簡單。
只需下列PL/SQL的宣告部分加上PRAGMA AUTONOMOUS_TRANSACTION 就可以了。
1.頂級的匿名PL/SQL塊
2.Functions 或 Procedure(獨立宣告或宣告在package中都可)
3.SQL Object Type的方法
4.觸發器。

比如:
在一個獨立的procedure中宣告AT
CREATE OR REPLACE PROCEDURE
Log_error(error_msg IN VARCHAR2(100))
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
Insert into Error_log values ( sysdate,error_msg);
COMMIT;
END;

下面我們來看一個例子,(win2000 advanced server + oracle8.1.6 , connect as scott)
建立一個表:
create table msg (msg varchar2(120));
首先,用普通的事務寫個匿名PL/SQL塊:
declare
cnt number := -1; --} Global variables
procedure local is
begin
select count(*) into cnt from msg;
dbms_output.put_line('local: # of rows is '||cnt);
insert into msg values ('New Record');
commit;
end;
begin
delete from msg ;
commit;
insert into msg values ('Row 1');
local;
select count(*) into cnt from msg;
dbms_output.put_line('main: # of rows is '||cnt);
rollback;
local;
insert into msg values ('Row 2');
commit;
local;
select count(*) into cnt from msg;
dbms_output.put_line('main: # of rows is '||cnt);
end;
執行結果(注意開啟serveroutput)
local: # of rows is 1 -> 子程式local中可以’看到’主匿名塊中的uncommitted記錄
main: # of rows is 2 -> 主匿名塊可以’看到’2條記錄(它們都是被local commit掉的)
local: # of rows is 2 -> 子程式local首先’看到’2條記錄,然後又commit了第三條記錄
local: # of rows is 4 -> 子程式local又’看到’了新增加的記錄(它們都是被local commit掉的),然後又commit了第五條記錄
main: # of rows is 5 -> 主匿名塊最後’看到’了所有的記錄.
從這個例子中,我們看到COMMIT和ROLLBACK的位置無論是在主匿名塊中或者在子程式中,都會影響到整個當前事務.
現在用AT改寫一下匿名塊中的procedure local:
...
procedure local is
pragma AUTONOMOUS_TRANSACTION;
begin
...
重新執行(注意開啟serveroutput)
local: # of rows is 0 -> 子程式local中無法可以’看到’主匿名塊中的uncommitted記錄 (因為它是獨立的)
main: # of rows is 2 -> 主匿名塊可以’看到’2條記錄,但只有一條是被commited.
local: # of rows is 1 -> 子程式local中可以’看到’它前一次commit的記錄,但是主匿名塊中的記錄已經被提前rollback了
local: # of rows is 3 -> 子程式local 中可以’看到’3條記錄包括主匿名塊commit的記錄
main: # of rows is 4 ->主匿名塊最後’看到’了所有的記錄.
很明顯,AT是獨立的,在它執行時,MT被暫停了. AT的COMMIT,ROLLBACK並不影響MT的執行.

運用AT時,有一些注意事項,簡單列舉如下:
1. 在匿名PL/SQL塊中,只有頂級的匿名PL/SQL塊可以被設為AT
2. 如果AT試圖訪問被MT控制的資源,可能有deadlock發生.
3. Package 不能被宣告為AT,只有package所擁有的function和procedure 才能宣告為AT
4. AT程式必須以commit 或rollback結尾,否則會產生Oracle
錯誤ORA-06519: active autonomous transaction detected and rolled back
在程式開發時,如果充分運用AUTONOMOUS TRANSACTION的特性,一定能取得事倍功半的效果.

參考資料:
metalink.oracle.com
oracle8i manual
log:
idle>truncate table msg;
Table truncated.
idle>---normal
idle>declare
2 cnt number := -1; --} Global variables
3 procedure local is
4 begin
5 select count(*) into cnt from msg;
6 dbms_output.put_line('local: # of rows is '||cnt);
7 insert into msg values ('New Record');
8 commit;
9 end;
10
11 begin
12 delete from msg ;
13 commit;
14 insert into msg values ('Row 1');
15 local;
16 select count(*) into cnt from msg;
17 dbms_output.put_line('main: # of rows is '||cnt);
18 rollback;
19
20 local;
21 insert into msg values ('Row 2');
22 commit;
23
24 local;
25 select count(*) into cnt from msg;
26 dbms_output.put_line('main: # of rows is '||cnt);
27 end;
28 /
local: # of rows is 1
main: # of rows is 2
local: # of rows is 2
local: # of rows is 4
main: # of rows is 5
PL/SQL procedure successfully completed.
idle>---AT
idle>declare
2 cnt number := -1; --} Global variables
3 procedure local is
4 pragma AUTONOMOUS_TRANSACTION;
5 begin
6 select count(*) into cnt from msg;
7 dbms_output.put_line('local: # of rows is '||cnt);
8 insert into msg values ('New Record');
9 commit;
10 end;
11
12 begin
13 delete from msg ;
14 commit;
15 insert into msg values ('Row 1');
16 local;
17 select count(*) into cnt from msg;
18 dbms_output.put_line('main: # of rows is '||cnt);
19 rollback;
20
21 local;
22 insert into msg values ('Row 2');
23 commit;
24
25 local;
26 select count(*) into cnt from msg;
27 dbms_output.put_line('main: # of rows is '||cnt);
28 end;
29 /
local: # of rows is 0
main: # of rows is 2
local: # of rows is 1
local: # of rows is 3
main: # of rows is 4
PL/SQL procedure successfully completed.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/756652/viewspace-242082/,如需轉載,請註明出處,否則將追究法律責任。

相關文章