Oracle存储过程关于在出现(自定义/自带)异常下out类型参数的获取问题的分析
✈️ 场景:
有一些关于金额和时间的精确且量大的计算需要在存储过程中完成。存储过程中有一些自定义的异常。并且将在RAISE前通过out类型的参数将详细的异常原因返回。
但是在通过Csharp的调用中,发现了很多的问题。需要书面总结。
💥问题点1:存储过程自身的问题
存储过程中发生异常时,在存储过程中发生异常无法获取out类型参数。
例表:
procedure:
create or replace procedure SP_TEST(EXC_MSG out varchar2)
as
ExcOne EXCEPTION; --自定义异常
ExcTwo EXCEPTION;
item varchar2(10);
cursor curOne is select ABBREVIATION from TB_CM_CPNAME;
begin
open curOne;
loop
fetch curOne into item;
exit when curOne%NOTFOUND;
IF(item='云久鴻') then
exc_msg := 'XXX错误'; --在异常raise前给out参数赋值
RAISE ExcOne; --raise异常
end if;
IF(item='海浪') then
exc_msg := 'YYY错误';
RAISE ExcTwo; --raise异常
end if;
end loop;
end;
测试执行语句:
SET SERVEROUTPUT ON;
declare
V_msg varchar2(20);
begin
SP_TEST(V_msg);
Exception --外部执行时尝试获取out参数
when others then
DBMS_OUTPUT.put_line('COUT<<'||v_msg);
end;
结果:
当在存储过程中去处理异常
修改存储过程:
create or replace procedure SP_TEST(EXC_MSG out varchar2)
as
ExcOne EXCEPTION; --自定义异常
ExcTwo EXCEPTION;
item varchar2(10);
cursor curOne is select ABBREVIATION from TB_CM_CPNAME;
begin
open curOne;
loop
fetch curOne into item;
exit when curOne%NOTFOUND;
IF(item='云久鴻') then
exc_msg := 'XXX错误';
RAISE ExcOne; --raise异常
end if;
IF(item='海浪') then
exc_msg := 'YYY错误';
RAISE ExcTwo; --raise异常
end if;
end loop;
EXCEPTION --在SP内处理异常
when ExcOne then
ROLLBACK;
--RAISE 注意不在再向上抛异常,不然还是获取不到out参数
when ExcTwo then
ROLLBACK;
end;
测试:
💥问题点2 :在外部调用中获取out参数
关于:
一般不会写了个存储过程只通过sql去调用,大部分情况是通过其他的外部语言来调用。所以对于外部调用来获取out参数又是一个需要注意的问题。
🌀使用C#调用oracle存储过程
Dapper调用存储过程
namespace UnitTest
{
[TestFixture]
public class TestDual
{
private IServiceProvider provider;
[SetUp]
public void SetUp()
{
var collection = new ServiceCollection();
//这是一个Dapper实现的执行sql工具
collection.AddSingleton(new SqlDapperService("XXXX", Db_Type.DbName.Oracle,
"XXXX","XXXX" ));
provider = collection.BuildServiceProvider();
}
[Test]
public void RealTest()
{
SqlDapperService dapper = provider.GetService<SqlDapperService>();
string EXC_MSG = $@"传参前";
int res = dapper.ExcuteNonQuery("sp_test", new { EXC_MSG }, CommandType.StoredProcedure);
Console.WriteLine($@"COUT<<"+EXC_MSG);
}
}
}
测试结果:并没有获取到out类型参数的值
ADO.NET 原生方式获取
/// <summary>
/// ADO.NET 测试调用存储过程
/// </summary>
[TestFixture]
public class ADO_test
{
[Test]
public void TestOne()
{
using(var conn = new OracleConnection(Encrypter.DecryptAES("XXXXX",
"xxxx", "xxxxx")))
{
conn.Open();
OracleCommand command = conn.CreateCommand();
command.CommandText = "SP_TEST";
command.CommandType = System.Data.CommandType.StoredProcedure;
//设置out参数
OracleParameter outparm = command.Parameters.Add("EXC_MSG", OracleDbType.Varchar2, ParameterDirection.Output);
outparm.Size = 200; //参数的长度
command.ExecuteNonQuery();
Console.WriteLine(outparm.Value) ;输出
}
}
}
小节
如果想要在Oracle的存储过程发生异常时获取out类型的参数,需要
- 在SP中处理异常,且不要再向上抛出(再次抛出会导致out类型参数的值被清除)
- 在数据库外部调用时,尽量选择原生的调用方式