一、 概念
嵌套事务(Nested Transaction): 指在主事务(MT)中嵌套的一个或多个子事务,并且子事务与主事务相互影响。
自治事务(Autonomous Transaction):由主事务调用但又独立于主事务,子事务对commit和rollback进行自治管理,不影响主事务执行效果。常用于写入LOG或TRACE信息便于查找错误。
二、 嵌套事务
1.预备Create Table
create table TEST_POLICY
(
POLICY_CODE VARCHAR2(20),
POLICY_TYPE CHAR(1)
);
2. 创建一个嵌套事务procedure
Procedure P_Insert_Policy(I_Policy_code varchar2(20),I_Policy_type char(1)) as
cnt number :=0;
begin
select count(1) into cnt from Test_Policy;
Dbms_Output.put_line('records of the test_policy is '|| cnt);
Insert into Test_Policy values(I_Policy_code, I_Policy_type);
commit;--commit in nested transaction
end P_Insert_Policy;
--call procedure used in nested transaction
PROCEDURE TEST_PL_SQL_ENTRY(I_POL_ID IN VARCHAR2,O_SUCC_FLG OUT VARCHAR2) AS
strSql varchar2(500);
cnt number := 0;
BEGIN
delete from test_policy;
commit;
insert into test_policy values('2010042101', '1');
select count(1) into cnt from Test_Policy;
Dbms_Output.put_line('records of the test_policy is '|| cnt);
--call nested transaction
P_Insert_Policy('2010042102', '2');
rollback;--rollback data for all transactions
commit;--master transaction commit
select count(1) into cnt from Test_Policy;
Dbms_Output.put_line('records of the test_policy is '|| cnt);
rollback;
select count(1) into cnt from Test_Policy;
Dbms_Output.put_line('records of the test_policy is '|| cnt);
END TEST_PL_SQL_ENTRY;
运行输出结果如下:
- records of the test_policy is 1 –-主事务中的操作已经commit
- records of the test_policy is 1 –-主事务的操作对Nested transaction有影响。
- records of the test_policy is 2 –-Nested transaction 已经Commit
- records of the test_policy is 2 –-Nested transaction对主事务有影响。
将上面的nested transaction的procedure修改一下,不进行commit:
Procedure P_Insert_Policy(I_Policy_code T_contract_master.Policy_Code%type,
I_Policy_type t_contract_master.policy_type%type) as
cnt number :=0;
begin
select count(1) into cnt from Test_Policy;
Dbms_Output.put_line('records of the test_policy is '|| cnt);
Insert into Test_Policy values(I_Policy_code, I_Policy_type);
--commit;
end P_Insert_Policy;
PROCEDURE TEST_PL_SQL_ENTRY(I_POL_ID IN VARCHAR2, O_SUCC_FLG OUT VARCHAR2) AS
strSql varchar2(500);
cnt number := 0;
BEGIN
delete from test_policy;
commit;
insert into test_policy values('2010042101', '1');
select count(1) into cnt from Test_Policy;
Dbms_Output.put_line('records of the test_policy is '|| cnt);
P_Insert_Policy('2010042102', '2');
rollback;
commit;
select count(1) into cnt from Test_Policy;
Dbms_Output.put_line('records of the test_policy is '|| cnt);
rollback;
select count(1) into cnt from Test_Policy;
Dbms_Output.put_line('records of the test_policy is '|| cnt);
END TEST_PL_SQL_ENTRY;
运行输出结果如下:
- records of the test_policy is 1 –-主事务中的操作已经commit
- records of the test_policy is 1 –-主事务的操作对Nested transaction有影响。
- records of the test_policy is 0 –-Nested transaction 的数据被主事务rollback.
- records of the test_policy is 0
三、 自治事务
以下类型的 PL/SQL blocks 可以被定义为自治事务:
- Stored procedures and functions.
- Local procedures and functions defined in a PL/SQL declaration block.
- Packaged procedures and functions.
- Type methods.
- Top-level anonymous blocks.
定义方法非常简单,在 DECLARE 后加上 PRAGMA AUTONOMOUS_TRANSACTION 即可。
来看一个简单的例子,注意以下在一个会话内执行
CREATE TABLE at_test (
id NUMBER NOT NULL,
description VARCHAR2(50) NOT NULL
);
INSERT INTO at_test (id, description) VALUES (1, 'Description for 1');
INSERT INTO at_test (id, description) VALUES (2, 'Description for 2');
-- 不要提交
SELECT * FROM at_test;
ID DESCRIPTION
---------- --------------------------------------------------
1 Description for 1
2 Description for 2
2 rows selected.
-- 自治事务plsql block
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
cnt number := 0;
BEGIN
select count(*) into cnt from at_test;
Dbms_Output.put_line('cnt: '|| cnt); -- 输出0,因为自治事务不受主事务影响
FOR i IN 3 .. 10 LOOP
INSERT INTO at_test (id, description)
VALUES (i, 'Description for ' || i);
END LOOP;
COMMIT;
select count(*) into cnt from at_test;
Dbms_Output.put_line('cnt: '|| cnt); -- 输出8,插入8行
END;
/
-- 回到主事务查询,一共十行(提交的自治事务影响主事务)
SELECT * FROM at_test;
ID DESCRIPTION
---------- --------------------------------------------------
1 Description for 1
2 Description for 2
3 Description for 3
4 Description for 4
5 Description for 5
6 Description for 6
7 Description for 7
8 Description for 8
9 Description for 9
10 Description for 10
10 rows selected.
-- 回滚,再查询,发现主事务插入的2行没了,自治事务插入的还在
ROLLBACK;
SELECT * FROM at_test;
ID DESCRIPTION
---------- --------------------------------------------------
3 Description for 3
4 Description for 4
5 Description for 5
6 Description for 6
7 Description for 7
8 Description for 8
9 Description for 9
10 Description for 10
8 rows selected.
再看一个与前面嵌套事务对应的例子:
create Procedure p_insert_policy_new(i_policy_code Varchar2(20), i_policy_type char(1)) as
Pragma Autonomous_Transaction; --定义自治事务
cnt number := 0;
begin
select count(1) into cnt from test_policy;
Dbms_Output.put_line('records of the test policy table is: '||cnt);
Insert into Test_Policy values(I_Policy_code, I_Policy_type);
commit;
select count(1) into cnt from test_policy;
Dbms_Output.put_line('records of the test policy table is: '||cnt);
end p_insert_policy_new;
/
--call auto trans procedure
create PROCEDURE TEST_PL_SQL_ENTRY(I_POL_ID IN VARCHAR2, O_SUCC_FLG OUT VARCHAR2) AS
strSql varchar2(500);
cnt number := 0;
v_policyCode t_contract_master.policy_code%type;
BEGIN
delete from test_policy;
commit;
insert into test_policy values('2010042101', '1');
select count(1) into cnt from Test_Policy;
Dbms_Output.put_line('records of the test_policy is '|| cnt);
p_insert_policy_new('2010042102', '2');
select count(1) into cnt from Test_Policy;
Dbms_Output.put_line('records of the test_policy is '|| cnt);
rollback;
select policy_code into v_policyCode from test_policy;
Dbms_Output.put_line('policy_code: '|| v_policyCode);
select count(1) into cnt from Test_Policy;
Dbms_Output.put_line('records of the test_policy is '|| cnt);
END TEST_PL_SQL_ENTRY;
/
运行结果如下:
- records of the test_policy is 1 -- 主事务插入数据
- records of the test policy table is: 0 -- 自治事务不受主事务影响
- records of the test policy table is: 1 -- 自治事务提交
- records of the test_policy is 2 -- 提交的自治事务影响主事务
- policy_code: 2010042102 -- 主事务回滚,自治事务已提交数据不受影响
- records of the test_policy is 1 -- 同上,行数为1
下面是一个用自治事务收集报错日志信息的例子:
定义一个自治事务存储过程
CREATE OR REPLACE PROCEDURE log_errors (p_error_message IN VARCHAR2) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO error_logs (id, log_timestamp, error_message)
VALUES (error_logs_seq.NEXTVAL, SYSTIMESTAMP, p_error_message);
COMMIT;
END;
/
外层代码调用存储过程,报错时记录日志
BEGIN
INSERT INTO at_test (id, description) VALUES (998, 'Description for 998');
-- Force invalid insert.
INSERT INTO at_test (id, description) VALUES (999, NULL);
EXCEPTION
WHEN OTHERS THEN
log_errors (p_error_message => SQLERRM);
ROLLBACK;
END;
/
SELECT * FROM at_test WHERE id >= 998;
no rows selected
SELECT * FROM error_logs;
ID LOG_TIMESTAMP ERROR_MESSAGE
---------- ----------------------- -----------------------------------------------
1 28-FEB-2006 11:10:10.107625 ORA-01400: cannot insert NULL into ("TIM_HALL"."AT_TEST"."DESCRIPTION")
参考
https://oracle-base.com/articles/misc/autonomous-transactions