1概述
(在一个会话中)事务何时开始:当数据库遇到第一个DML(UPDATE/INSERT/DELETE/MERGE)语句。
(在一个会话中)事务何时结束:结束于第一个COMMIT/ROLLBACK/DDL/GRANT/REVOKE。对于DLL/GRANT/REVOKE,这些命令的内部嵌套了一个COMMIT语句。
上面的两句话是基本教义,就好比8荣8耻之于和谐社会一样重要。
2事务级与语句级
CREATE TABLE t_trans_sample(fnum NUMBER(3));
insert into t_trans_sample values(2); --sql-2
根据教义,sql-1,sql-2属于同一事务,此时如果我们执行commit/rollback,两条语句将同时被提交/回滚,即‘事务级’提交/回滚。
如果是这样呢:
insert into t_trans_sample values(‘a’); --sql-2
sql-2出错,此时只有出错的语句被回滚(当然受该语句影响的可能是多行数据),sql-1仍然处于‘未提交’状态,即 ‘语句级’回滚。
总结一下:用户显式执行的提交/回滚,通常总是‘事务级’的;当语句出错时由Oracle自动执行的回滚,通常总是‘语句级’的。
3隐式保存点
begin
insert into t_trans_sample values(2); --sql-2
insert into t_trans_sample values(‘a’); --sql-3
end;
此时的结果是sql-2也被自动回滚了,只剩下sql-1的结果处于未提交状态。为什么,不是说应该是语句级的么?这是因为,执行PL/SQL匿名块、或者调用存储过程,会在紧挨着调用之前插入一个隐式的savepoint,当块内部出错并自动回滚时,并非是语句级的,而是回滚到此savepoint为止。
但是,如果语句块中有exception处理模块:
begin
insert into t_trans_sample values(2); --sql-2
insert into t_trans_sample values(‘a’); --sql-3
exception
when others then
null;
end;
此时只有sql-3被回滚,sql-1跟sql-2仍处于未提交状态。
还有一点需要注意,savepoint只针对回滚的情况,对于提交操作无影响,下面示例中的commit会将三条结果都提交:
begin
insert into t_trans_sample values(2); --sql-2
insert into t_trans_sample values(3); --sql-3
commit;
end;
4触发器与事务
触发器代码中不允许发出任何事务控制语句,触发器与触发该触发器的SQL语句同属于一个事务,它只能随着外部事务的提交/回滚而提交/回滚。如果触发器内的DML(如果有的话)出错,则此DML被回滚,同时引发此触发器的DML也将被回滚。
(但在oracle8i 以及更高的版本中,你可以创建作为自治事务而执行的触发器,在这种情况下,触发器可以做提交或回滚操作,而与触发该触发器的外部SQL所在的事务无关,自治事务见后文)
5 Db link与事务
Oracle可以透明地处理分布式事务,意思就是,即便一个事务中涉及的表可能位于不同的oracle server上(通过Db link进行访问),对事务本身没有任何影响。这里就不作示例测试了。
6自治事务
普通的事务是无法进行嵌套的,例如你有一个大事务涉及10条DML,你想先commit中间的3条是无法实现的。自治事务的出现很好地解决了这个问题,它可以使嵌套中的事务保持各自的独立,对比下面两个例子:
(1)
begin
insert into t_trans_sample values(2);
commit;
end;
/
(2)
declare
pragma autonomous_transaction;
begin
insert into t_trans_sample values(2);
commit;
end;
/
(1)中两条语句都被提交,这在前面的文章里已经提过了,(2)中只有第二个insert语句被提交,第一个仍然处于未提交的状态,可以看到自治事务的效果。在实际应用中,自治事务常常用于记录错误日志,通常来说,记录错误日志的事务应该独立出来,不应该与主事务杂揉在一起,这种情况下就应该使用自治事务了。