Oracle: какова ситуация с использованием RAISE_APPLICATION_ERROR?

Мы можем использовать RAISE для запуска исключения. В каких конкретных ситуациях нам нужно использовать RAISE_APPLICATION_ERROR?

Спасибо.

Ответы

Ответ 1

Для RAISE_APPLICATION_ERROR используется два использования. Во-первых, заменить общие сообщения об исключениях Oracle своими собственными, более значимыми сообщениями. Во-вторых, мы создаем собственные условия исключения, когда Oracle не бросит их.

Следующая процедура иллюстрирует оба способа использования. Он применяет бизнес-правило, в котором новые сотрудники не могут быть наняты в будущем. Он также отменяет два исключения Oracle. Один из них - DUP_VAL_ON_INDEX, который генерируется уникальным ключом на EMP(ENAME). Другое - это исключение, определяемое пользователем, если внешний ключ между EMP(MGR) и EMP(EMPNO) нарушен (поскольку менеджер должен быть уже существующим сотрудником).

create or replace procedure new_emp
    ( p_name in emp.ename%type
      , p_sal in emp.sal%type
      , p_job in emp.job%type
      , p_dept in emp.deptno%type
      , p_mgr in emp.mgr%type 
      , p_hired in emp.hiredate%type := sysdate )
is
    invalid_manager exception;
    PRAGMA EXCEPTION_INIT(invalid_manager, -2291);
    dummy varchar2(1);
begin
    -- check hiredate is valid
    if trunc(p_hired) > trunc(sysdate) 
    then
        raise_application_error
            (-20000
             , 'NEW_EMP::hiredate cannot be in the future'); 
    end if;

    insert into emp
        ( ename
          , sal
          , job
          , deptno
          , mgr 
          , hiredate )
    values      
        ( p_name
          , p_sal
          , p_job
          , p_dept
          , p_mgr 
          , trunc(p_hired) );
exception
    when dup_val_on_index then
        raise_application_error
            (-20001
             , 'NEW_EMP::employee called '||p_name||' already exists'
             , true); 
    when invalid_manager then
        raise_application_error
            (-20002
             , 'NEW_EMP::'||p_mgr ||' is not a valid manager'); 

end;
/

Как выглядит:

SQL> exec new_emp ('DUGGAN', 2500, 'SALES', 10, 7782, sysdate+1)
BEGIN new_emp ('DUGGAN', 2500, 'SALES', 10, 7782, sysdate+1); END;

*
ERROR at line 1:
ORA-20000: NEW_EMP::hiredate cannot be in the future
ORA-06512: at "APC.NEW_EMP", line 16
ORA-06512: at line 1

SQL>
SQL> exec new_emp ('DUGGAN', 2500, 'SALES', 10, 8888, sysdate)
BEGIN new_emp ('DUGGAN', 2500, 'SALES', 10, 8888, sysdate); END;

*
ERROR at line 1:
ORA-20002: NEW_EMP::8888 is not a valid manager
ORA-06512: at "APC.NEW_EMP", line 42
ORA-06512: at line 1


SQL>
SQL> exec new_emp ('DUGGAN', 2500, 'SALES', 10, 7782, sysdate)

PL/SQL procedure successfully completed.

SQL>
SQL> exec new_emp ('DUGGAN', 2500, 'SALES', 10, 7782, sysdate)
BEGIN new_emp ('DUGGAN', 2500, 'SALES', 10, 7782, sysdate); END;

*
ERROR at line 1:
ORA-20001: NEW_EMP::employee called DUGGAN already exists
ORA-06512: at "APC.NEW_EMP", line 37
ORA-00001: unique constraint (APC.EMP_UK) violated
ORA-06512: at line 1

Обратите внимание на другой вывод двух вызовов на RAISE_APPLICATION_ERROR в блоке ИСКЛЮЧЕНИЯ. Установка необязательного третьего аргумента в значение TRUE означает, что RAISE_APPLICATION_ERROR включает в себя триггерное исключение в стеке, которое может быть полезно для диагностики.

В в руководстве пользователя PL/SQL есть более полезная информация.

Ответ 2

Вы используете RAISE_APPLICATION_ERROR, чтобы создать исключение/ошибку стиля Oracle, специфичное для вашего кода/потребностей. Хорошее использование этих средств позволяет создавать более четкие, более удобные в обслуживании и более легкие отладки.

Например, если у меня есть приложение, вызывающее хранимую процедуру, которая добавляет пользователя, и этот пользователь уже существует, вы обычно получите ошибку, например:

ORA-00001: unique constraint (USERS.PK_USER_KEY) violated

Очевидно, что эта ошибка и связанное сообщение не уникальны для задачи, которую вы пытались сделать. Создание собственных ошибок приложения Oracle позволяет вам более четко определить цель действия и причину проблемы.

raise_application_error(-20101, 'User ' || in_user || ' already exists!');

Теперь ваш код приложения может написать обработчик исключений, чтобы обработать это конкретное условие ошибки. Подумайте об этом как о способе заставить Oracle сообщать об ошибках, которые ваше приложение ожидает в "языке" (из-за отсутствия лучшего термина), которое вы определили и более значимом для вашего проблемного домена приложения.

Обратите внимание, что пользовательские ошибки должны быть в диапазоне от -20000 до -20999.

Следующая ссылка содержит много полезной информации по этой теме и исключений Oracle вообще.

Ответ 3

Чтобы подробнее рассказать об ответах Генри, вы также можете использовать определенные коды ошибок, начиная с raise_application_error и обрабатывать их соответственно на стороне клиента. Например:

Предположим, что у вас была процедура PL/SQL, подобная этой, чтобы проверить наличие записи местоположения:

   PROCEDURE chk_location_exists
   (
      p_location_id IN location.gie_location_id%TYPE
   )
   AS
      l_cnt INTEGER := 0;
   BEGIN
      SELECT COUNT(*)
        INTO l_cnt
        FROM location
       WHERE gie_location_id = p_location_id;

       IF l_cnt = 0
       THEN
          raise_application_error(
             gc_entity_not_found,
             'The associated location record could not be found.');
       END IF;
   END;

Функция raise_application_error позволяет вам поднять конкретный код ошибки. В заголовке пакета вы можете определить:   gc_entity_not_found INTEGER := -20001;

Если вам нужны другие коды ошибок для других типов ошибок, вы можете определить другие коды ошибок, используя -20002, -20003 и т.д.

Затем на стороне клиента вы можете сделать что-то вроде этого (этот пример для С#):

/// <summary>
/// <para>Represents Oracle error number when entity is not found in database.</para>
/// </summary>
private const int OraEntityNotFoundInDB = 20001;

И вы можете выполнить свой код в try/catch

try
{
   // call the chk_location_exists SP
}
catch (Exception e)
{
    if ((e is OracleException) && (((OracleException)e).Number == OraEntityNotFoundInDB))
    {
        // create an EntityNotFoundException with message indicating that entity was not found in
        // database; use the message of the OracleException, which will indicate the table corresponding
        // to the entity which wasn't found and also the exact line in the PL/SQL code where the application
        // error was raised
        return new EntityNotFoundException(
            "A required entity was not found in the database: " + e.Message);
    }
}

Ответ 4

если ваше приложение принимает ошибки, исходящие из Oracle, тогда вы можете его использовать. у нас есть приложение, каждый раз, когда возникает ошибка, мы вызываем raise_application_error, приложение будет всплывать красным полем, чтобы показать сообщение об ошибке, которое мы предоставляем с помощью этого метода.

При использовании dotnet-кода я просто использую "повышение", исключение dotnet. Механизм автоматически фиксирует ошибку, переданную Oracle ODP, и отображается внутри моего кода исключения catch.