PL\SQL用户指南与参考6.3 转载
如图所示,自治事务在提交时它所做的内容变化对其它事务是可见的。当恢复到主事务的时候变化内容对主事务也是可见的,但这需要把它的隔离级别被设置为READ COMMITTED(默认)。

如果我们像下面一样把主事务的隔离级别设置为SERIALIZABLE,恢复主事务时,由它的自治事务所做的变化对主事务就不可见了。
SET?TRANSACTION?ISOLATION?LEVEL?SERIALIZABLE;使用保存点
保存点的作用域就是定义它的事务。定义在主事务中的保存点与定义在自治事务中的保存点没有任何关系。实际上,主事务和一个自治事务是可以使用相同的保存点名称的。
我们只能回滚到当前事务中标记的保存点。所以,当在一个自治事务中,我们不能回滚到主事务中标记的保存点。要是想这样做的话,我们就必须用已存在的自治程序恢复主事务。
在主事务中,如果回滚到一个在我们开启一个自治事务之前标记的保存点,那么回滚操作并不会影响到自治事务。记住,自治事务完全独立于主事务。
避免错误为了避免一些公共错误,在设计自治事务时一定要记住以下几点:
- 如果一个自治事务要访问主事务(自治程序退出之前是不能恢复的)拥有的资源,就可能发生死锁。那样的话,Oracle就会在自治事务中抛出异常,如果异常未被捕获,主事务就会被回滚。 Oracle初始化参数TRANSACTIONS指定了并行事务的最大数量。如果自治事务(与主事务并行执行)没有被考虑的话这个数字就有可能被超出。 如果我们没有使用提交或回滚操作退出一个活动自治事务,Oracle就会抛出一个异常。如果异常未被捕获,事务就会回滚。
4、使用自治触发器
很多时候,我们可以使用数据库触发器记下事件。假定我们要跟踪一个数据表所有的插入操作,即使是那些后来被回滚掉的。在下例中,我们用触发器把重复的行插入到一个影像表(shadow table)。由于是自治触发器,所以,触发器能把插入的内容提交到影像表中,无论我们是否把插入内容提交到主表中。
--?create?a?main?table?and?its?shadow?table
CREATE?TABLE?parts?(pnum?NUMBER(4),?pname?VARCHAR2(15));
CREATE?TABLE?parts_log?(pnum?NUMBER(4),?pname?VARCHAR2(15));
--?create?an?autonomous?trigger?that?inserts?into?the
--?shadow?table?before?each?insert?into?the?main?table
CREATE?TRIGGER?parts_trig
??BEFORE?INSERT
??ON?parts
??FOR?EACH?ROW
DECLARE
??PRAGMA?AUTONOMOUS_TRANSACTION;
BEGIN
??INSERT?INTO?parts_log
???????VALUES?(:NEW.pnum,?:NEW.pname);
??COMMIT;
END;--?insert?a?row?into?the?main?table,?and?then?commit?the?insert
INSERT?INTO?parts
?????VALUES?(1040,?'Head?Gasket');
COMMIT?;
--?insert?another?row,?but?then?roll?back?the?insert
INSERT?INTO?parts
?????VALUES?(2075,?'Oil?Pan');
ROLLBACK?;
--?show?that?only?committed?inserts?add?rows?to?the?main?table
SELECT?*?FROM?parts?ORDER?BY?pnum;
PNUM?PNAME
-------?---------------
1040?Head?Gasket
--?show?that?both?committed?and?rolled-back?inserts?add?rows
--?to?the?shadow?table
SELECT?*?FROM?parts_log?ORDER?BY?pnum;
PNUM?PNAME
-------?---------------
1040?Head?Gasket
2075?Oil?Pan
不同于常规触发器的是,自治触发器还能使用本地动态SQL执行DDL语句。下例中,触发器bonus_trig在表bonus更新后,删除临时表temp_bonus:
CREATE?TRIGGER?bonus_trig
??AFTER?UPDATE
??ON?bonus
DECLARE
??PRAGMA?AUTONOMOUS_TRANSACTION;???--?enables?trigger?to?perform?DDL
BEGIN
??EXECUTE?IMMEDIATE?'DROP?TABLE?temp_bonus';
END;
5、从SQL中调用自治函数
从SQL语句中调用的函数,必须遵守控制副作用的规则。为了检查是否与规则相冲突,可以使用编译指示RESTRICT_REFERENCES。它的作用是判断函数是否读写数据表或打包变量。
但是,在定义的时候,自治程序总不会与规则"不读数据库"(RNDS)和"不写数据库"(WNDS)相冲突,即使事实上是冲突的。这样的特性是很有用,在下面的例子中,当我们从查询中调用打包函数log_msg时,它能向数据表debug_output插入数据,而且还不与规则"不写数据库"冲突:
--?create?the?debug?table
CREATE?TABLE?debug_output?(msg?VARCHAR2(200));
--?create?the?package?spec
CREATE?PACKAGE?debugging?AS
??FUNCTION?log_msg?(msg?VARCHAR2)
????RETURN?VARCHAR2;
??PRAGMA?RESTRICT_REFERENCES?(log_msg,?WNDS,?RNDS);
END?debugging;
--?create?the?package?body
CREATE?PACKAGE?BODY?debugging?AS
??FUNCTION?log_msg?(msg?VARCHAR2)
????RETURN?VARCHAR2?IS
????PRAGMA?AUTONOMOUS_TRANSACTION;
??BEGIN
????--?the?following?insert?does?not?violate?the?constraint
????--?WNDS?because?this?is?an?autonomous?routine
????INSERT?INTO?debug_output
?????????VALUES?(msg);
????COMMIT;
????RETURN?msg;
??END;
END?debugging;
--?call?the?packaged?function?from?a?query
DECLARE
??my_empno???NUMBER?(4);
??my_ename???VARCHAR2?(15);
BEGIN
??...
??SELECT?debugging.log_msg?(ename)
????INTO?my_ename
????FROM?emp
???WHERE?empno?=?my_empno;
??--?even?if?you?roll?back?in?this?scope,?the?insert
??--?into?'debug_output'?remains?committed?because
??--?it?is?part?of?an?autonomous?transaction
??IF?...?THEN
????ROLLBACK;
??END?IF;
END;
十、确保PL/SQL程序的向后兼容
PL/SQL第二版允许使用一些不再使用的非正常功能:
- 声明变量时,可以对记录和表类型向前引用 在函数说明的RETURN子句中指定变量(非类型)名称 使用IN模式的参数为index-by表的元素赋值 把一个IN模式的记录中的一个字段作为另一个子程序的OUT模式参数 把一个OUT模式的记录中的一个字段放置于赋值符号的右边 在SELECT语句的FROM列表中使用OUT模式参数
为了向后兼容,我们可以设置PLSQL_V2_COMPATIBILITY标识来保留第二版的这些功能,在服务器端有两种方法设置这个标识:
- 把下面行添加到Oracle初始化文件中:
PLSQL_V2_COMPATIBILITY=TRUE执行下面的语句:
ALTER?SESSION?SET?plsql_v2_compatibility?=?TRUE;
ALTER?SYSTEM?SET?plsql_v2_compatibility?=?TRUE;
如果我们把标识指定为FALSE(默认的),就不能使用非正常功能。在客户端,命令行选项可以设置标识。例如,使用Oracle预编译程序,我们就可以在命令行中指定运行时选项DBMS。