代码改变世界

关于Oracle AUTONOMOUS TRANSACTION(自治事务)的介绍(整理)

2015-12-10 14:53  silence_blog  阅读(555)  评论(0编辑  收藏  举报

UTONOMOUS 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 varchar(100))
is
 pragma autonomous_transaction;
begin
  insert into Error_log values(system,error_msg);
  commit;
end;

下面我们来看一个例子:

  建立一个表:

create table msg(ms varchar2(12));

  由于下面会用到输出,而serveroutput默认为off状态,so

  打开serveroutput输出:

set serveroutput on; --用于模块中的输出 dbms_output.put_line();

  首先,用普通的事务写个匿名PL/Sql块:

declare
  cnt number:=-1;
  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;

  运行结果:

匿名块已完成
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又’看到’了新增加的记录,然后又commit了第五条记录
main: # of rows is: 5 -> 主匿名块最后’看到’了所有的记录.

上面的例子可以看出,commit和rollback的位置无论是在主匿名块中或者是在子程序中,都会影响到当前事务。

下面用AT改写一下匿名块中的procedure local:

……
  procedure local is
  pragma AUTONOMOUS_TRANSACTION;
  begin
……

  运行结果:

匿名块已完成
local: #of rows is: 0 ->子程序local中无法'看到'主匿名块中的uncommite记录(因为它是独立的)
main: # of rows is: 2 ->主匿名块可以'看到'2条记录,但只有一条是commit的。
local: #of rows is: 1 ->子程序local中可以'看到'1 条记录,是它之前commit的,主匿名块中的记录,已经被rollback
local: #of rows is: 3 ->子程序local中可以'看到'3条记录,其中2 条是被local commit的,一条是主匿名块commit的
main: # of rows is: 4 ->主匿名块最后'看到'了所有的记录.

显然,AT是独立的,在它执行时,MT被暂停了,AT的commit,rollback并不影响 MT的执行。

 

如果上面的匿名块不是很清楚,不能让你看明白程序的执行过程,你可以建立一个存储过程,将其包裹起来,然后单步调试跟踪运行,存储过程如下:

create or replace procedure test1
as
cnt number:=-1;
procedure local is
pragma autonomous_transaction;
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 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; end;

  

运用AT时,有一些注意事项,简单列举如下:

1.在匿名pl/sql块中,只有顶级的匿名pl/sql块可以被设为AT

2.如果AT视图访问MT控制的资源,可能有deallock发生。

3.package不能被声明为AT,只有package所拥有的function和procedure才能声明 AT

4.AT程序必须以commit或rollback结尾,否则会产生Oracle错误ORA-06519:active autonomous_transaction detected and rolled back

在程序开发中,如果充分运用autonomous transaction的特性,一定能取得事半功倍的效果。