- 论坛徽章:
- 0
|
author :seao
------------------------------------------------------------------------------------------
在这里详细论证一下异常和事务之间如何控制:在plsql程序设计中与事务有关的对象无外乎就如下3种 LSQL(block),procedure,trigger,
1 LSQL(block) + Procedure
2 LSQL(bolck) + trigger
3 LSQL(block) + Procedure or (Function) + trigger
第一步搭建试验环境:
create table test (
a1 number,
a2 varchar2(20)
);
select * from test;
A1 A2
---------- --------------------
第二步:
1> LSQL(block) + Procedure
a)CREATE OR REPLACE PROCEDURE test_p1(a test.a1%TYPE,b test.a2%TYPE)
IS
BEGIN
INSERT INTO test VALUES(a,b);
raise_application_error('-20001','raise a exception');
END;
BEGIN
INSERT INTO test VALUES(1,'a');
test_p1(2,'b');
END;
结果:存储过程抛出异常给被调用对象(block),该块没有做异常捕捉,则异常抛给(调用其block的环境),即-plsql developer工具,先前的事务(两天insert语句,自动回滚).
b)更改该存储过程如下
CREATE OR REPLACE PROCEDURE test_p1(a test.a1%TYPE,b test.a2%TYPE)
IS
BEGIN
INSERT INTO test VALUES(a,b);
raise_application_error('-20001','raise a exception');
INSERT INTO test VALUES(3,'c');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('handle 2001 exception');
END;
BEGIN
INSERT INTO test VALUES(1,'a');
test_p1(2,'b');
INSERT INTO test VALUES(4,'d');
END;
结果:异常被捕捉,异常前面的事务和plsql块的黄色部分都可有效手动提交.蓝色部分不会执行
C)再次更改如下:
CREATE OR REPLACE PROCEDURE test_p1(a test.a1%TYPE,b test.a2%TYPE)
IS
BEGIN
INSERT INTO test VALUES(a,b);
raise_application_error('-20001','raise a exception');
INSERT INTO test VALUES(3,'c');
END;
BEGIN
INSERT INTO test VALUES(1,'a');
test_p1(2,'b');
INSERT INTO test VALUES(4,'d');-不会运行
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('handle 2001 exception');
--commit or rollback;
END;
总结: 如果异常被所在块所捕获处理,CALLING对象程序会继续进行,该事务对先前和以后的DML操作都有效;反之,如果异常没有所在块所捕获处理(由calling对象处理),则calling先前事务是可控制的.
2> LSQL(bolck) + trigger
Create table test2 (num number);
a:
CREATE OR REPLACE TRIGGER test_trigger
AFTER INSERT OR UPDATE OR DELETE ON test
DECLARE
v NUMBER DEFAULT 1;
BEGIN
INSERT INTO test2 VALUES(1);
dbms_output.put_line('trigger is working');
END;
Declare
Insert into test (1,’a’);
End;
说明:建立test表的UML语句级触发器,执行pssql块后,可成功手动提交该事务中的两条insert语句.
b:修改后
CREATE OR REPLACE TRIGGER test_trigger
AFTER INSERT OR UPDATE OR DELETE ON test
DECLARE
v NUMBER DEFAULT 1;
BEGIN
INSERT INTO test2 VALUES(v);
dbms_output.put_line('trigger is working');
raise_application_error('-20002','raise a exception from this trigger'); END;
BEGIN
INSERT INTO test VALUES(1,'a');
END;
说明:只要异常没有被捕捉,则程序将报错,先前执行事务语句,全部由系统自动回滚.
c:再修改
CREATE OR REPLACE TRIGGER test_trigger
AFTER INSERT OR UPDATE OR DELETE ON test
DECLARE
v NUMBER DEFAULT 1;
BEGIN
INSERT INTO test2 VALUES(v);
dbms_output.put_line('trigger is working');
raise_application_error('-200 02','raise a exception from this trigger');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('handle it');
END;
BEGIN
INSERT INTO test VALUES(1,'a');
END;
说明:异常被自身所处理,程序可正常运行.事务可成功手动提交.
d:再修改
CREATE OR REPLACE TRIGGER test_trigger
AFTER INSERT OR UPDATE OR DELETE ON test
DECLARE
v NUMBER DEFAULT 1;
BEGIN
INSERT INTO test2 VALUES(v);
dbms_output.put_line('trigger is working');
raise_application_error('-20002','raise a exception from this trigger');
END;
BEGIN
INSERT INTO test VALUES(1,'a');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('handle it');
--commit or rollback;
END;
说明:此时即使是”calling”对象作了例外处理,那也无法控制先前的DML事务.
为什么呢?我认为像先前类似这种只要父对象接收处理子对象就可以保证事务的顺利提交-属于calling objet revoke called object,结构.而触发器这个对象比较特殊,它不属于这种模式.可以这么理解:因为触发器是伴随着DML语句的,触发器的执行失败可视为该DML语句没有执行成功,所以该DML语句不属于该事务.但是其他表的事务控制语句不影响事务控制,我们接着看如下的例子:
e:再修改
CREATE OR REPLACE TRIGGER test_trigger
AFTER INSERT OR UPDATE OR DELETE ON test
BEGIN
raise_application_error('-20002','raise a exception from this trigger');
END;
BEGIN
INSERT INTO test2 VALUES(1);
INSERT INTO test2 VALUES(2);
INSERT INTO test VALUES(1,'abc');出现异常,该条DML语句应视为无效.
INSERT INTO test2 VALUES(3);既然出现异常,这条语句不会运行.
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('handle it');
--commit or rollback; 此时事务可控制,但只对非触发器的表有效.
END;
第三种:其实就是一,二的混合.就不再论述了.
如有问题请及时反馈,谢谢! |
|