如何在Oracle存储过程发生异常时获取out类型参数的值

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类型参数的值被清除)
  • 在数据库外部调用时,尽量选择原生的调用方式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗马苏丹默罕默德

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值