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. |