【问题标题】:raise_application_error exception handling in subproceduresraise_application_error 子过程中的异常处理
【发布时间】:2015-01-14 13:36:45
【问题描述】:

我的数据库中有多个存储过程调用多个存储过程。举一个小例子,我在下面构建了其中一些的虚构版本。在我的例子中,一个Java程序调用calculate_bill,它调用calculate_commission,它又调用update_record

我希望就如何最好地将错误消息向上传播到调用 Java 层获得一些建议,以便用户获得与错误发生位置相对应的精确错误消息。

我真的很坚持这一点。在我的示例中,我使用raise_application_error 进行了玩弄,只是不断地将其洗牌。我在下面的远程操作方式是否正确?还是相关程序中的一个 raise_application_error 就足够了,不需要pragma exception init 等?

为了说明我的意思,在下面的示例中,如果用户输入的数字对应于由于不存在而无法更新的记录,我希望他们收到消息: “计算账单错误。计算佣金错误。不存在要更新的记录”或类似的内容。

那么两个问题:

  1. 为应用层的最终用户在堆栈中向上传递错误消息的最佳实践、最有效、最整洁的方法是什么?
  2. 是否有人对代码输出更整洁有任何建议,即连接这些错误以使其更有意义的最佳方法?我非常愿意接受有关如何使这项工作最好的任何建议,因为我完全没有这方面的经验。

示例: (代码错误):

-20000 : Error in top level procedure
-20001 : Error in middle level procedure
-20002 : Error in bottom level procedure

Java 代码:

try {
    // call calculate_bill
exception (SQLException ex)
    // output oracle code and relevant message.

Oracle 代码:

create or replace procedure calculate_bill(in_num NUMBER) 
is
error_calculating_commission EXCEPTION;
error_updating_record EXCEPTION;
PRAGMA EXCEPTION_INIT (error_calculating_commission, -20001);
PRAGMA EXCEPTION_INIT (error_updating_record , -20002);
begin
    if in_num > 2 then
        calculate_commission(in_num);
else
    raise_application_error(-20000, 'Error calculating bill. ' || 'Record number doesn''t exist.', false);
end if;
exception
    when error_calculating_commission then
        raise_application_error(SQLCODE, 'Error calculating bill. ' || SQLERRM, false);
    when error_updating_record then
        raise_application_error(SQLCODE, 'Error calculating bill. ' || SQLERRM, false);
    when others then
        raise_application_error(-20000, 'Unknown error encountered calculating bill.', false);
end;

create or replace procedure calculate_commission(in_num NUMBER) 
is
begin
    if in_num < 30 then
        raise_application_error(-20001, 'Number too small to calculate commission.', false);
    elsif in_num >= 30 and < 40 then
        declare
            error_storing_record EXCEPTION;
            PRAGMA EXCEPTION_INIT (error_storing_record , -20002);
        begin
            update_record(in_num);
        exception
        when error_storing_record then
             raise_application_error(SQLCODE, 'Error calculating commission. ' || SQLERRM, false);
        when others then
             raise_application_error(-20001, 'Unknown error encountered calculating commission.', false);
    else
        raise_application_error(-20001, 'Number too large to calculate commission', false);
    end if;
end;

create or replace procedure update_record(in_num NUMBER) 
is
begin
  //some SQL query with a where clause, where in_num equals something
exception
when no_data_found then
    raise_application_error(-20002, 'No record exists to be updated', false);
when others then
    raise_application_error(-20002, 'Unknown error encountered updating record.', false);
end if;
end;

注意:我知道这个例子有点做作。我只是想保持简短。

【问题讨论】:

  • 一种方法是通过存储过程接口本身作为输出参数传递错误代码和描述。这样做应该会给您更多的控制权,并且您会更少地受到 Oracle 的错误处理渗透能力的限制。我不在 Oracle 上工作,所以我不会谈论它可能完全支持的其他设计。希望甲骨文专家能参与进来。
  • 我希望如此,因为您的建议让我转了一圈!当我开始时,这正是我正在做的事情,我被警告不要这样做,因为将错误作为参数传递被认为是不好的做法。这是我的“替代”方法,我正在寻找整理帮助!

标签: sql oracle exception plsql exception-handling


【解决方案1】:

我实现这一点的方法是在错误实际发生的地方使用RAISE_APPLICATION_ERROR,然后在其他层中不处理它(或者,如果你想登录数据库,请在OTHERS 部分中捕获它,然后使用RAISE 而不是RAISE_APPLICATION_ERROR 重新加注。

您是OTHERS 部分仍然存在您在上一个问题中提出的问题:当发生未知错误时,您将用一般的、无用的消息替换它。套用Tom Kyte,“不以RAISE 结尾的WHEN OTHERS 是一个错误`。

在没有登录数据库的情况下,我会像这样重写提供的代码:

create or replace procedure calculate_bill(in_num NUMBER) 
is
begin
    if in_num > 2 then
        calculate_commission(in_num);
else
    raise_application_error(-20000, 'Error calculating bill. ' || 'Record number doesn''t exist.', false);
end if;
end;

create or replace procedure calculate_commission(in_num NUMBER) 
is
begin
    if in_num < 30 then
        raise_application_error(-20001, 'Number too small to calculate commission.', false);
    elsif in_num >= 30 and in_num < 40 then
        update_record(in_num);
    else
        raise_application_error(-20001, 'Number too large to calculate commission', false);
    end if;
end;

create or replace procedure update_record(in_num NUMBER) 
is
v_stub number;
begin
  select 1 into v_stub from dual where 1 = 0;
exception
when no_data_found then
    raise_application_error(-20002, 'No record exists to be updated', false);
end;

以下是示例输入和他们创建的堆栈跟踪:

exec calculate_bill(0)
ORA-20000: Error calculating bill. Record number doesn't exist.
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 7
ORA-06512: at line 1

exec calculate_bill(10)
ORA-20001: Number too small to calculate commission.
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 5
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1

exec calculate_bill(35)
ORA-20002: No record exists to be updated
ORA-06512: at "SCHEMANAME.UPDATE_RECORD", line 8
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 7
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1

exec calculate_bill(100)
ORA-20001: Number too large to calculate commission
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 9
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1

如果您要在这些过程中添加错误日志记录,只需将 OTHERS 子句添加到 EXCEPTION 部分即可:

   WHEN OTHERS THEN
      my_logging (SQLCODE, SQLERRM, DBMS_UTILITY.format_error_backtrace ());
      RAISE;

RAISE_APPLICATION_ERROR 调用创建的新异常将由RAISE 透明处理。

【讨论】:

  • 这看起来很棒。最后一个问题。假设在查找存储过程或表时出现问题。这通常会在“当其他人”块中处理。在您的示例中,您是否会简单地记录、引发它并让调用 Java 应用程序确定如何处理可能发生并陷入其他错误的各种可能错误?
  • 我不确定您所说的“查找存储过程的问题”是什么意思。如果引用的过程不存在(或者您没有访问权限),则引用它的任何内容都不会编译,因此尝试在异常块中处理它是没有意义的。
猜你喜欢
  • 2020-12-19
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-24
  • 1970-01-01
相关资源
最近更新 更多