在oracle中使用“嵌套”事务

我在Oracle中遇到了麻烦。 我有一些这样的程序:

create or replace procedure myschema.DataSave(v_value IN NUMBER) as begin SET TRANSACTION ISOLATION LEVEL READ COMMITTED; begin insert/update/delete... exception when OTHERS then goto error; end; COMMIT; return; <> ROLLBACK; return; end; / 

我以这种forms从c#项目调用此过程:

 ... string conn_str = "..."; OracleConnection con = new OracleConnection(conn_str); con.Open(); OracleCommand cmd = new OracleCommand("", con); try { cmd.Transaction = cmd.Connection.BeginTransaction(); for (int i = 0; i < 10; i++) { // this condition simulates incorrect situations if (i == 5) { throw new Exception("Something is wrong."); } cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "myschema.DataSave"; cmd.Parameters.Clear(); cmd.Parameters.Add("v_value", OracleDbType.Int32, i, ParameterDirection.Input); } cmd.Transaction.Commit(); } catch (Exception ex) { cmd.Transaction.Rollback(); } finally { con.Close(); con.Dispose(); } ... 

所以我试图在数据库层上使用一个“内部”或“嵌套”事务,在应用程序层上使用另一个“外部”事务。 但是当抛出应用程序中的exception时,回滚不起作用(先前检索的数据 – 1,2,3,4 – 保留在数据库中)。 但为什么? 我不必使用此表单中的mssql和存储过程来解决此问题:

 create procedure myschema.DataSave @id as int as begin begin transaction insert/update/delete... if @@error > 0 goto error commit transaction return error: rollback transaction return end go 

我是Oracle的新手,无法找到与此类似的解决方案。 请有人告诉我我做错了什么。

Oracle不支持嵌套事务。 如果事务提交,则提交。 这就是为什么您通常不希望在存储过程中提交(或回滚)事务,这使得如果您的事务语义不同,很难在其他地方重用该过程。

但是,您可以在过程开始时声明一个保存点,并在出现错误时回滚到该保存点。 如果然后删除提交,则事务仅由应用程序代码控制,而不是由数据库代码控制

 begin savepoint beginning_of_proc; insert/update/delete... exception when OTHERS then rollback to beginning_of_proc; raise; end; 

但是,在这种情况下,我的偏见是代码中没有保存点,没有回滚,除非你正在做一些有用的事情,否则不要捕获exception。 只需执行DML,抛出任何exception,并在应用程序中处理它们。

例外仅限于他们加入的计划组。

 create or replace procedure myschema.DataSave(v_value IN NUMBER) as ex_dml_error EXCEPTION; begin begin insert/update/delete... exception when OTHERS then ex_dml_error; end; COMMIT; EXCEPTION WHEN ex_dml_error THEN ROLLBACK; end; /