l 事务嵌套测试案例
(1)首先,建立一个表:
create table msg (msg varchar2(100));
(2)然后,建立子事务存储过程local。
create or replace procedure local is
cnt number := -1; -- Global variables
begin
select count(*) into cnt from msg;
dbms_output.put_line('local: # of rows is '||cnt);
insert into msg values ('New Record');
commit;
end;
(3)最后,用如下PL/SQL匿名块调用local;
declare
cnt number := -1;
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;
运行结果(注意打开set serveroutput on)如下:
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-> 主匿名块最后’看到’了所有的记录.
PL/SQL 过程已成功完成。
(4)重建过程local,将commit改为rollback,如下:
create or replace procedure local is
cnt number := -1; -- Global variables
begin
select count(*) into cnt from msg;
dbms_output.put_line('local: # of rows is '||cnt);
insert into msg values ('New Record');
rollback;
end;
执行第(3)步的PL/SQL匿名块,
运行结果(注意打开set serveroutput on)如下:
local: # of rows is 1
main: # of rows is 0-> 子程序把主匿名块的一条记录也rollback了,所以结果为0
local: # of rows is 0
local: # of rows is 1-> 主匿名块把自己的一条记录成功commt,子程序也可以看到
main: # of rows is 1
PL/SQL 过程已成功完成。
(5)重建过程local,将commit去掉,如下:
create or replace procedure local is
cnt number := -1; -- Global variables
begin
select count(*) into cnt from msg;
dbms_output.put_line('local: # of rows is '||cnt);
insert into msg values ('New Record');
end;
执行第(3)步的PL/SQL匿名块,
运行结果(注意打开set serveroutput on)如下:
local: # of rows is 1-> 子程序看到主匿名块uncommited的一条记录
main: # of rows is 2-> 主匿名块可以看到主匿名块和子程序插入的uncommited记录
local: # of rows is 0-> 主匿名块把主匿名块和子程序插入的记录全部rollback
local: # of rows is 2-> 主匿名块把主匿名块和子程序插入的记录全部commit
main: # of rows is 3
PL/SQL 过程已成功完成。
l AUTONOMOUS TRANSACTION(自治事务)的介绍
AUTONOMOUS_TRANSACTION是指在function,procedure等subprograms中对事务进行自治管理,当在别的pl/sql block里去调用这些subprograms的时候这些subprograms并不随着父pl/sql block的失败而回滚,而是自己管自己commit。自治事务常用于写入LOG或TRAC信息便于查找错误。
自治事务(以下简称AT)是由主事务(以下简称MT)调用但是独立于MT的事务。在自治事务被调用执行时,MT被挂起,在自治事务内部,一系列的DML可以被执行并且commit或rollback. 自治事务防止嵌套提交,使事务在自己的事务区内提交或回滚不会影响其他的事务。由于自治事务的独立性,它的commit和rollback并不影响MT的执行效果。在自治事务执行结束后,主事务获得控制权,又可以继续执行了。
实现自治事务的定义,只需下列PL/SQL的声明部分加上PRAGMA AUTONOMOUS_TRANSACTION 就可以了。
1. 顶级的匿名PL/SQL块
2. Functions 或 Procedure
3. 触发器。
比如:
在一个独立的procedure中声明自治事务:
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;
现在用自治事务改写一下procedure local:
create or replace procedure local is
cnt number := -1; -- Global variables
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;
执行第(3)步的PL/SQL匿名块,
运行结果(注意打开set serveroutput on)如下:
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 ->主匿名块最后’看到’了所有的记录.
PL/SQL 过程已成功完成。
l 结论:
从这个例子中我们看到,在没有自制事务的情况下,COMMIT和ROLLBACK的位置无论是在主匿名块中或者在子程序中,都会影响到整个当前事务. 如果子程序中有事务或有commit语句,那么是回滚不了的,否则可以回滚,因为事务只是回滚上一个committ或rollback之后的所有事务。
在使用自制事务的情况下, AT是独立的,在它执行时,MT被暂停了. AT的COMMIT,ROLLBACK并不影响MT的执行.
运用AT时,有一些注意事项,简单列举如下:
1. 在匿名PL/SQL块中,只有顶级的匿名PL/SQL块可以被设为AT
2. Package 不能被声明为AT,只有package所拥有的function和procedure 才能声明为AT
3. AT程序必须以commit 或rollback结尾,否则会产生Oracle错误ORA-06519: active autonomous transaction detected and rolled back