当其他人然后异常处理时



背景:

我使用了一些 Oracle 文章来开发一个错误包,它由五个过程组成。

其中两个是Log_And_ReturnLog_And_Continue。他们在整个程序中被调用。每个都接受输入并将其传递给 Handle 过程。例如:

PROCEDURE Log_And_Return (error_name)
IS
BEGIN
    Handle (error_name, TRUE, TRUE);
END Log_And_Return; 

然后,Handle 过程根据传递给它的变量调用 Log 过程和 Raise_To_Application 过程,如下所示:

PROCEDURE Handle (error_name, log_error, reraise_error)    
IS
BEGIN
    // Code to fetch error code and message using error_name input parameter.
    IF log_error THEN
        LOG (error_code, error_message);
    END IF;
    IF in_reraise_error THEN
        Raise_To_Application (error_code, error_message);
    END IF;    
END Handle;

日志过程存储日期,堆栈跟踪,错误代码,错误消息和id,最后Raise_To_Application过程执行所述:

raise_application_error (error_code, error_message);

问题:

我的问题是这个。假设我有一个执行查询的过程,例如获取客户记录。如果此查询失败,则是一个大问题。所以我可以这样做:

BEGIN    
    SELECT *something*
    FROM *some table*
    WHERE *some field* = *some user input*
    // more logic
EXCEPTION
WHEN NO_DATA_FOUND THEN
    ERR.Log_And_Return('unknown_id');  
WHEN OTHERS THEN
    ERR.Log_And_Return('unknown_error');  
END;

在这里,我的Log_And_Return过程接受输入,转到表并返回要向用户显示的字符串。如果查询找不到用户的记录,我有一个特定的错误,还有一个未知错误的一般错误。在这两种情况下,都会执行日志记录,以获取错误的完整堆栈跟踪。

但是,在我的示例中,我有一个"//更多逻辑"部分。假设,我将代码修改为:

BEGIN    
    SELECT *something* INTO *some variable*
    FROM *some table*
    WHERE *some field* = *user id*
    Call_Another_Procedure(*user id*, *some variable*)
EXCEPTION
WHEN NO_DATA_FOUND THEN
    ERR.Log_And_Return('unknown_id');  
WHEN OTHERS THEN
    ERR.Log_And_Return('unknown_error');  
END;
现在,在选择查询

之后,我将使用选择查询的结果调用另一个过程。在这个查询中,我正在做一些事情,包括更新语句,如下所示:

// bunch of logic
BEGIN
    UPDATE *another table*
    SET *some field* = *some value*
    WHERE *some field* = *variable passed into method*
EXCEPTION
WHEN NO_DATA_FOUND THEN
    Err.Log_And_Return('some_error')
END;

问题:

我的问题是,如果查询未返回任何结果,我会抛出NO_DATA_FOUND错误,我记录问题,然后在我的"Raise_To_Application"过程中引发应用程序错误......然后,它将被父过程中的"当其他人"子句捕获,这将向用户返回错误的消息。

对此的解决方法是什么?注意:如果需要发布更多代码,请告诉我。

编辑:

我考虑过的一种解决方法,我不知道是否建议这样做,是用 BEGIN END EXCEPTION 块包装每个存储过程,其中每个过程都有一个"当其他人"块,该块刚刚记录并重新引发最近的错误(即使用 SQLCODE)。然后,在我的应用程序层中,我可以指定如果错误介于 -20000 和 -20999 之间,则将其与其消息一起显示,否则显示通用消息(DBA 可以通过查看日志表以及完整的堆栈跟踪来找出数据库中发生的情况)。对此有什么想法吗?

编辑 2:

如果有什么没有意义的地方,我可以澄清。我大量更改和简化了代码,以删除 id 参数和其他一些内容。

这几乎是我一直在使用的方法,因为我想在我的代码中记录每个入口和出口点:

application_error EXCEPTION;
PRAGMA EXCEPTION_INIT (application_error, -20000);
BEGIN    
    SELECT *something* INTO *some variable*
    FROM *some table*
    WHERE *some field* = *user id*
    Call_Another_Procedure(*user id*, *some variable*)
EXCEPTION
WHEN NO_DATA_FOUND THEN
    ERR.Log_And_Return('unknown_id');
WHEN application_error THEN -- ordinary exception raised by a subprocedure
    ERR.Log_And_Return('application_error');
    RAISE;
WHEN OTHERS THEN
    ERR.Log_And_Return('unknown_error');
    RAISE;
END;

对于子程序:

BEGIN
    UPDATE *another table*
    SET *some field* = *some value*
    WHERE *some field* = *variable passed into method*
EXCEPTION
WHEN NO_DATA_FOUND THEN
    Err.Log_And_Return('some_error');  -- this raises ORA-20000
END;

对于when_others异常,请考虑使用 AFTER SERVERERROR 触发器。像波纹管这样的东西

create or replace trigger TRG_SERVERERROR 
   after servererror on database
declare
   <some_variable_for_logging_the_call_stack>
begin
   ERR.Log;
end;

我将引用Tom Kytes的话,当时他被允许提交三个PL/SQL新功能的请求,这就是他所说的

我抓住了这个机会。我的第一个建议很简单,"删除 当其他人从语言中得到子句时。

您还可以阅读Tom Kyte的以下文章 - 为什么你真的想让异常传播

UPD:在您的情况下,解决方案的整个工作流程如下(在我的主观意见中)

我会建议不包括其他人。我更喜欢收到不友好的错误消息,而不是无缝消息 - 类似于"哎呀,出了点问题。在一天结束时,您还可以将所有意外异常包装到应用程序层上用户的某些消息中,并包装有关数据库的详细信息,以免被第三方使用等。

我的建议是有一些错误。

create or replace package ERR
   ci_NoDataFound constant int := -20100;
   NoDataFound exception;
   pragma exception_init(NoDataFound, -20100);
   procedure Raise;
   procedure Log;
end P_PRSRELIAB;

在父过程中,您将处理当前特定过程的执行,而不处理其他过程。

BEGIN    
   SELECT *something* INTO *some variable*
   FROM *some table*
   WHERE *some field* = *user id*
   Call_Another_Procedure(*user id*, *some variable*)
EXCEPTION
   WHEN NO_DATA_FOUND THEN
      ERR.Raise(-20100, 'unknown user id');         
END;

从父过程调用的过程将仅处理此特定过程的执行。

BEGIN    
   SELECT *something*
   FROM *some table*
   WHERE *some field* = *some user input*
EXCEPTION
   WHEN NO_DATA_FOUND THEN
      ERR.Raise(-20100, 'unknown some user input');  
END;

在应用层,我们将有适当的消息 - "未知某些用户输入"或"未知用户ID"。另一方面,触发器将记录有关特定异常的所有信息。

您需要使用 RAISE 重新引发基础过程中的错误。

发生error时,如果您有exception block,则句柄将移动到exception blockcaller将保持不知道状态,直到您使用 RAISE re-raise它。

将所有基础过程保留在块BEGIN-END内。

此外,使用 dbms_utility.format_error_stackdbms_utility.format_error_backtrace 获取调用堆栈。

最新更新