(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Об исключениях и правилах

Источник: oracle
Стивен Фернстайн

Автор: Стивен Фернстайн, член коллегии Oracle ACE

Где, когда и как лучше всего обрабатывать исключения

Вопрос: Недавно я узнал, что если исключение возникает в разделе объявлений блока, то раздел обработки исключений этого блока не обрабатывает его. Это кажется неверным. Почему PL / SQL работает именно так, и что это значит для моей практики программирования?

Ответ: Если исключение возникает в разделе объявлений блока, то оно распространяется во внешнюю среду необработанным.

PL / SQL ведет себя таким образом (или, если быть более точным, команда разработчиков PL / SQL решила реализовать обработку исключений именно таким образом) потому, что до тех пор пока локальные переменные и константы полностью не обработаны, программа не является жизнеспособной. Предположим, что исключение, возникшее в разделе объявлений, будет обрабатываться внутри этой подпрограммы. На что можно будет сослаться внутри раздела исключений? Ведь нельзя быть уверенным, что локальные переменные были проинициализированы.

Ключевой вопрос: как это поведение влияет на способ написания кода? Перед тем, как ответить на этот вопрос, давайте посмотрим, когда мы можем столкнуться с этой проблемой.

Исключения в разделе объявлений происходят, когда вы пытаетесь инициализировать переменную, объявленную в этом разделе, таким способом, который вызывает исключение. Наиболее часто возникающим исключением является, конечно, ORA -06502 или VALUE _ ERROR , которое возникает (назовем два сценария) при попытке присвоить символьной переменной значение, которое слишком велико для неё, либо когда вы пытаетесь присвоить числовой переменной нечисловое значение. Например:

DECLARE

   l_name VARCHAR2(5) := 'STEVEN';

   l_age NUMBER := '49 Years Old';

BEGIN

То же самое правило для исключений применяется при инициализации переменных, объявленных в пакете (вне любой подпрограммы). Если исключение возникает при попытке инициализировать переменную уровня пакета, то это исключение будет распространяться необработанным за пределы пакета, даже если раздел инициализации содержит раздел обработки исключений. В такой ситуации машина PL / SQL все же регистрирует пакет как инициализированный и позволяет ссылаться на подпрограммы и переменные пакета.

Чтобы понять это, рассмотрим следующую последовательность шагов и PL / SQL операторов:

1.     Я компилирую пакет valerr , который присваивает слишком длинное значение пакетной символьной переменной. Тело пакета включает раздел обработки исключений (см. листинг 1).

2.     Листинг 1: В пакете присваивается слишком длинное значение пакетной символьной переменной.

3.     PACKAGE valerr

4.     IS

5.         FUNCTION little_name RETURN VARCHAR2;

6.     END valerr;

7.      

8.     PACKAGE BODY valerr

9.     IS

10.      g_name       VARCHAR2 (1)       :=           'Liu';

11.   

12.      FUNCTION little_name RETURN VARCHAR2

13.      IS

14.      BEGIN

15.                     RETURN g_name;

16.      END little_name;

17.  BEGIN

18.      DBMS_OUTPUT.put_line ('Before I show you the name... ');

19.  EXCEPTION

20.      WHEN OTHERS

21.      THEN

22.                     DBMS_OUTPUT.put_line (          'Trapped the error: ' //             DBMS_UTILITY.format_error_stack ()

23.                                                                                                                                                );

24.                     RAISE;

25.  END valerr;

26.  Теперь я пытаюсь запустить функцию valerr . little _ name , исключение не обрабатывается

[у автора: " the exception goes unhandled ", по моему, как раз наоборот. А.Бачин ]:

SQL>           BEGIN

  2                              DBMS_OUTPUT.put_line

('Name: ' // valerr.little_name);

  3               END;

  4               /

BEGIN

*

ERROR at line 1:

ORA-06502: PL/SQL: numeric or value

error: character string buffer too small

ORA-06512: at "HR.VALERR", line 3

ORA-06512: at line 2

Этого и следовало ожидать.

27.  Но когда я пытаюсь вызвать эту функцию второй раз, исключение не возбуждается:

28.  SQL>           BEGIN

29.    2                                DBMS_OUTPUT.put_line

30.  ('Name: ' // Valerr.little_name);

31.    3               END;

32.    4               /

33.   

34.  Name:

35.  PL/SQL procedure successfully completed.

Пакет был помечен как инициализированный, и исполнительный механизм PL / SQL не пытается снова инициализировать его, поэтому код в разделе инициализации никогда не выполняется. Однако все же есть возможность вызывать все программы этого пакета, что может привести к изрядной путанице и впечатлению, что ошибку нельзя воспроизвести.

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

Обратите внимание, что Oracle рассматривает это поведение как баг (номер 5658561). Если вы столкнулись с таким поведением и хотите, чтобы Oracle изменил его, предлагаю вам зайти на Oracle MetaLink и добавить к этому багу свое сообщение о том, как текущее поведение наносит ущерб вашему приложению.

Должна ли эта информация об обработке исключений изменить ваш способ написания кода? Я полагаю, да. Может быть иногда необходимо, чтобы исключение из раздела объявлений распространялось необработанным, хотя, вероятно, в большинстве случаев, предпочтительней перехватить исключение внутри блока и зарегистрировать информацию об ошибке.

Сделать это просто: не присваивайте переменным значения по умолчанию в разделе объявлений. Вместо этого создайте локальную процедуру инициализации и присвойте значения по умолчанию в этой процедуре. Затем вызовите процедуру инициализации в первой строке исполняемого раздела программы, тогда любое исключение можно будет обработать в этой программе. Например, вместо того, чтобы писать процедуру, подобную этой:

PROCEDURE process_data

IS

    l_name   VARCHAR2 (10) :=

                   'Steven Feuerstein';

BEGIN

    DBMS_OUTPUT.put_line (l_name);

EXCEPTION

    WHEN OTHERS

    THEN

                   DBMS_OUTPUT.put_line

                                  (  'Trapped the error: '

                                   // DBMS_UTILITY.format_

                                   error_stack ()

                                  );

                   RAISE;

END process_data;

напишите следующее:

PROCEDURE process_data

IS

    l_name   VARCHAR2 (10);

    PROCEDURE initialize

    IS

    BEGIN

                   l_name := 'Steven Feuerstein';

    END initialize;

BEGIN

    initialize;

    DBMS_OUTPUT.put_line (l_name);

EXCEPTION

    WHEN OTHERS

    THEN

                   DBMS_OUTPUT.put_line

                                  (  'Trapped the error: '

                                   // DBMS_UTILITY.format_

                                   error_stack ()

                                  );

    RAISE;

END process_data;

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

SQL>           BEGIN

    2                             process_data;

    3              END;

    4              /

Trapped the error: ORA-06502:

PL/SQL: numeric or value error:

character string buffer too small

BEGIN

*

ERROR at line 1:

ORA-06502: PL/SQL: numeric or value

error: character string buffer too small

ORA-06512: at "HR.PROCESS_DATA",

line 19

ORA-06512: at line 2

То же самое верно и для пакетов. В исправленной процедуре valerr  в Листинге 2, в разделе инициализации просто вызывается процедура инициализации.

 Листинг 2: В разделе инициализации вызывается процедура инициализации

  1               PACKAGE BODY valerr

 2 IS

 3               g_name    VARCHAR2 (1);

 4

 5               FUNCTION little_name

 6                               RETURN VARCHAR2

 7               IS

 8               BEGIN

 9                               RETURN g_name;

10                             END little_name;

11

12                             PROCEDURE initialize

13                             IS

14                             BEGIN

15                                             g_name := 'Lu';

16                             END initialize;

17               BEGIN

18                               initialize;

19               EXCEPTION

20                               WHEN OTHERS

21                               THEN

22                                              DBMS_OUTPUT.put_line ( 'Trapped the error: ' // DBMS_UTILITY.format_error_stack ()

23                                                                                                                                                             );

24         RAISE;

25               END valerr;

Теперь я должен признаться, что у меня есть два замечания относительно данного мной совета (переместить присваивание значений по умолчанию в отдельную подпрограмму инициализации). Во-первых, этому совету нельзя следовать по отношению к константам. Значения по умолчанию должны быть присвоены им во время объявления. Во-вторых, в исправленном пакете valerr (в Листинге 2) переменная g _ name объявлена в строке 3, а значение ей присваивается только в строке 15. В большинстве обычных пакетов, переменные будут также объявлены в первых строках пакета, а код инициализации будет отстоять от них на сотни, возможно, даже тысячи строк. Лично я не люблю такие расстояния.

Присваивание значений по умолчанию в той же строке, что и объявление переменной, кажется более простым для понимания. Вся информация в одном месте. Должен ли я поступиться этим удобством для чтения, чтобы улучшить обработку ошибок? Это обоснованная уступка, но здесь разумно использовать компромисс.

Вот что я предлагаю: если значение по умолчанию для переменной или константы является литералом, присвойте его при объявлении, а если значение по умолчанию является результатом вычисления выражения, переместите это присваивание в процедуру инициализации.

Этот подход улучшит читабельность кода, уменьшив риск возникновения необработанных исключений. Риск будет минимальным, поскольку мы обращаем достаточно внимания на свой код, когда пишем его, чтобы увидеть, что мы присваиваем значение неправильного типа или неверного размера. Например, если все переменные инициализируются литералами, то подпрограмма инициализации не понадобится (см. листинг 3).

 Листинг 3: Переменные инициализируются литеральными значениями.

PROCEDURE process_data

IS

    l_name                                                   VARCHAR2 (100) := 'Steven Feuerstein';

    l_books_sold                           PLS_INTEGER;

    PROCEDURE initialize

    IS

    BEGIN

                   l_books_sold := book_counter.in_circulation ('Oracle PL/SQL Programming');

    END initialize;

BEGIN

    initialize;

    DBMS_OUTPUT.put_line (

                                   l_name

      //           ' sold '

      //           l_books_sold

      //           ' books.');

EXCEPTION

    WHEN OTHERS

    THEN

                   q$error_manager.raise_unanticipated;

                   RAISE;

END process_data;

А чтобы перехватить исключения, связанные с объявлением, можно использовать оператор блока. Оператор блока - это раздел DECLARE - BEGIN - END , который размещается внутри раздела исключений. Поскольку этот блок может иметь свой собственный раздел обработки исключений, можно немедленно перехватить исключение, и либо обработать ошибку, либо зарегистрировать ее, и снова возбудить исключение.

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

Предположим, например, что в процедуре process _ data я не работаю с переменной 1_ books _ sold до строки 245 этой процедуры. Вместо того, чтобы объявлять эту переменную рядом с l _ name , которая используется в процедуре немедленно, я подожду до того момента, когда она потребуется в программе, и использую оператор блока. Тогда я смогу перехватить исключение, которое может возникнуть в разделе объявлений. Листинг 4 содержит исправленную процедуру process _ data , иллюстрирующую использование оператора блока.

Листинг 4: исправленная процедура PROCESS_DATA с оператором блока

PROCEDURE process_data

IS

    l_name     VARCHAR2 (100) := 'Steven Feuerstein';

BEGIN

    /*

    Немедленное использование l_name

    */

    IF l_name IS NOT NULL

    THEN

... много кода здесь ...

    END IF;

    /*

еще больше кода ...

Затем я использую оператор блока, чтобы объявить l _ books _ sold

прямо в той области программы, в которой она требуется.

*/

    <>

    DECLARE

                   l_books_sold                           PLS_INTEGER := book_counter.in_circulation ('Oracle PL/SQL Programming');

    BEGIN

                   IF l_books_sold > 1000

                   THEN

                                  ... много кода здесь ...

                   END IF;

    EXCEPTION

                   WHEN VALUE_ERROR

                   THEN

                                  q$error_manager.raise_unanticipated

                                                   ('Problem initializing l_books_sold!');

                                  RAISE;

    END check_books_sold;

   

    ... и много кода здесь ...

END process_data;

Один заключительный момент: начиная с Oracle Database 10 g Release 1, компилятор PL / SQL может выдавать рекомендации относительно качества кода. Например, он будет предупреждать нас, что некоторые строки кода в подпрограмме никогда не будут выполнены или являются "недостижимыми" ( PLW -6002). Было бы замечательно, если бы Oracle добавил рекомендацию для кода подобного этому:

DECLARE

    l_name VARCHAR2(5) := 'STEVEN';

    l_age NUMBER := '49 Years Old';

BEGIN

Тогда не придется ждать до запуска программы, чтобы обнаружить эту проблему.

Исходный код приложения в обработчике исключения.

Я убедился, что размещать исходный код приложения внутри обработчика исключения - плохой стиль программирования. Мы должны иметь возможность удалить все разделы обработки исключения, и, при отсутствии ошибок, наш код должен работать также. Но бывают ситуации, когда нужно выполнить SELECT INTO (явный однострочный запрос), ожидая, что он не вернет ни одной строки (иначе говоря, это правильный результат). Однако база данных Oracle в этом случае возвращает исключение NO _ DATA _ FOUND , и потом приходиться писать логику приложения в разделе исключений. Что же теперь никогда не использовать явный оператор SELECT INTO в своем коде?!

Совершенно верно : считается плохой практикой размещать что-либо в обработчике исключений, кроме кода обработки исключения. Если вы помещаете код приложения в предложение WHEN , другие разработчики должны знать, что нужно смотреть логику приложения в этом разделе. Поскольку это не является нормой, эта логика часто остается незамеченной.

Поэтому давайте согласимся, что размещать код приложения в предложении WHEN можно только для того, чтобы обработать ошибку (обычно такой код включает регистрацию и повторный вызов исключения). Справедливо заметить, что при использовании оператора SELECT INTO в исполняемом разделе, реализация этого подхода является загадкой. Значит ли это, что никогда нельзя писать SELECT INTO в своих программах? Давайте рассмотрим этот вопрос.

Явный оператор SELECT INTO вызывает NO _ DATA _ FOUND , если ни одна строка не выбрана и TOO _ MANY _ ROWS , если найдено более одной строки. Эти два исключения нуждаются в различной обработке. Ллевеллин предлагает разделить все исключения на три группы:

·         намеренные,

·         неуместные и

·         неожиданные.

Намеренными являются исключения, которые намеренно возбуждаются в программе, как часть нормального поведения. Прекрасным примером программы, которая вызывает намеренное исключение, является UTL _ FILE . GET _ LINE , которая вызывает NO _ DATA _ FOUND , когда чтение достигло конца файла.

Неуместными называются исключения, которые, возникая, не являются ошибкой в логике приложения. Это могут быть, например, разные условия, связанные с данными. Исключение NO _ DATA _ FOUND , вызванное SELECT INTO , является неуместным.

Возникшие "тяжелые ошибки", которых вы не ожидали и которые могут указывать на серьёзную проблему в приложении, являются неожиданными . TOO _ MANY _ ROWS - это классическая неожиданная ошибка; она означает, что для первичного или уникального ключа существует дублирующее значение.

Перед тем как начать создавать свое следующее приложение, определите стратегию обработки исключений для каждого из этих трех типов. Когда вы столкнетесь с конкретным исключением, выясните, в какую категорию оно попадает, и выполните соответствующее действие. Вот руководящие принципы, которым я следую для этих трех типов исключений:

A. Намеренные. Измените свою программу так, чтобы избежать размещения логики приложения в разделе исключений. Например, один из способов применить это правило к UTL _ FILE . GET _ LINE показан в процедуре process _ file на Листинге 5, которая читает содержимое файла и затем обрабатывает каждую прочитанную строку. Обратите внимание на цикл в строках с 16 по 18: этот цикл выглядит как бесконечный (он не содержит оператора EXIT ), но на самом деле он останавливается, когда UTL _ FILE вызывает NO _ DATA _ FOUND .

Листинг 5: Процедура PROCESS_FILE вызывает UTL_FILE.GET_LINE напрямую.

 1 PROCEDURE process_file (dir_in IN VARCHAR2, file_in IN VARCHAR2)

 2 IS

 3               TYPE line_t IS TABLE OF VARCHAR2 (32767)

 4                               INDEX BY PLS_INTEGER;

 5

 6               l_file                            UTL_FILE.file_type;

 7               l_lines                      line_t;

 8 BEGIN

 9               l_file :=

10                                             UTL_FILE.fopen         (LOCATION               => dir_in

11                                                 , filename                                                           => file_in

12                                                 , open_mode                                        => 'R'

13                                                 , max_linesize                                      => 32767

14                                                                                                                              ) ;

15

16                             LOOP

17                               UTL_FILE.get_line (l_file, l_lines (l_lines.COUNT + 1));

18                               END LOOP;

19               EXCEPTION

20                             WHEN NO_DATA_FOUND

21                             THEN

22                                             /* Обработка каждой строки */

23                                              FOR indx IN 1 .. l_lines.COUNT

24                                              LOOP

25                                                             do_stuff_with_line (l_lines (indx));

26                                              END LOOP;

27

28                                              UTL_FILE.fclose (l_file);

29  END process_file;

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

Никогда не вызывать UTL _ FILE . GET _ LINE напрямую! Листинг 6 показывает, как переписать процедуру, чтобы решить эту проблему. Я создаю локальный модуль get _ next _ line , который вызывает UTL _ FILE . GET _ LINE . Он перехватывает NO _ DATA _ FOUND и возвращает TRUE в выходном ( OUT ) булевском аргументе, если достигнут конец файла.

Листинг 6: исправленная процедура PROCESS_FILE вызывает локальный модуль

 1 PROCEDURE process_file (dir_in IN VARCHAR2, file_in IN VARCHAR2)

 2 IS

 3               TYPE line_t IS TABLE OF VARCHAR2 (32767)

 4                               INDEX BY PLS_INTEGER;

 5

 6               l_file                                       UTL_FILE.file_type;

 7               l_lines                      line_t;

 8               l_eof                                       BOOLEAN                                                := FALSE;

 9

10                             PROCEDURE get_next_line (line_out OUT VARCHAR2, eof_out OUT BOOLEAN)

11                             IS

12                             BEGIN

13                                             UTL_FILE.get_line (l_file, line_out);

14                                             eof_out := FALSE;

15                             EXCEPTION

16                                             WHEN NO_DATA_FOUND

17                                             THEN

18                                                            line_out := NULL;

19                                                            eof_out  := TRUE;

20                             END get_next_line;

21               BEGIN

22                             l_file :=

23                                             UTL_FILE.fopen (LOCATION                                     => dir_in

24                                                             , filename                                                             => file_in

25                                                             , open_mode                                          => 'R'

26                                                             , max_linesize                         => 32767

27                                                                                                                                        );

28

29                             WHILE (NOT l_eof)

30                             LOOP

31                                             get_next_line (l_lines (l_lines.COUNT + 1), l_eof);

32                                             EXIT WHEN l_eof;

33                             END LOOP;

34

35                             /* Обработка каждой строки */

36                             FOR indx IN 1 .. l_lines.COUNT

37                             LOOP

38                                             do_stuff_with_line (l_lines (indx));

39                             END LOOP;

40

41                             UTL_FILE.fclose (l_file);

42 END process_file;

Затем я пишу цикл WHILE , который показывает, при каких обстоятельствах цикл будет завершаться. Сразу после цикла я завершаю прочую логику приложения и закрываю файл.

Код, который придется написать, чтобы обработать намеренное исключение, конечно, будет различным для каждого исключения (и обстоятельств, которые его вызывают).

B. Неожиданные . Руководящие принципы для обработки неожиданных исключений очень просты. В общем случае, необходимо зарегистрировать ошибку, вместе со всей конкретной информацией о приложении, которая поможет понять, что ее вызвало. И затем нужно вновь вызвать ошибку, чтобы предотвратить дальнейшее выполнение внешнего блока.

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

C. Неуместные . Теперь давайте обсудим, что делать с неуместными исключениями, такими как NO _ DATA _ FOUND . Как и в случае с намеренными исключениями, основное правило - избегать размещения логики приложения в разделе исключений. Реализовать это при работе с неуместными исключениями можно, дав программисту возможность выбрать: должно ли исключение возбуждаться или нет.

Чтобы продемонстрировать этот подход в случае с NO _ DATA _ FOUND , допустим, что Сэм написал программу, которая возвращает ID для отдела с заданным именем:

FUNCTION id_for_name (

    department_name_in IN departments

                   .department_name%TYPE

)

    RETURN departments.department_id%TYPE

IS

    l_return departments

    .department_id%TYPE;

BEGIN

    SELECT department_id

                   INTO l_return

                   FROM departments

    WHERE department_name =

                   department_name_in;

    RETURN l_return;

END id_for_name;

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

PROCEDURE load_from_staging_table

IS

    l_id departments.department_id%TYPE;

BEGIN

    FOR dept_rec IN (SELECT *

                   FROM dept_staging_table)

    LOOP

                   BEGIN

                                  l_id := id_for_name

                                                 (dept_rec.department_name);

                                  submit_update_request (dept_rec);

                   EXCEPTION

                                  WHEN NO_DATA_FOUND

                                  THEN

                                                 submit_add_request (dept_rec);

                   END;

    END LOOP;

END load_from_staging_table;

Если название отдела не найдено в таблице, функция id _ for _ name вызывает NO _ DATA _ FOUND . Поэтому Сандра создает анонимный блок внутри цикла, перехватывая исключение, помещая логику "запроса на добавление нового отдела" ( submit _ add _ request ) в разделе исключений, и продолжает работать.

Однако это именно то, чего мы хотели избежать: логика приложения в разделе обработки исключений. И вновь, что делать программисту?

Чтобы устранить этот недостаток, перепишите функцию id _ for _ name и, заодно, все однострочные запросы и функции поиска (смотри листинг 7). Этот подход имеет несколько основных черт. Первое , новый параметр propagate _ if _ ndf _ in указывает, должно ли исключение NO _ DATA _ FOUND (когда оно вызвано оператором SELECT INTO ) распространяться за пределы функции.

Листинг 7: Исправленная функция ID_FOR_NAME 

 1 FUNCTION id_for_name (

 2               department_name_in               IN                           departments.department_name%TYPE

 3 ,             propagate_if_ndf_in  IN                           BOOLEAN := FALSE

 4 ,             ndf_value_in                                                          IN                departments.department_id%TYPE := NULL

 5 )

 6               RETURN departments.department_id%TYPE

 7 IS

 8               l_return    departments.department_id%TYPE;

 9 BEGIN

10                             SELECT department_id

11                                INTO l_return

12                               FROM departments

13                             WHERE department_name = department_name_in;

14

15                             RETURN l_return;

16               EXCEPTION

17                             WHEN NO_DATA_FOUND

18                             THEN

19                                             IF propagate_if_ndf_in

20                                             THEN

21                                                            RAISE;

22                                             ELSE

23                                                            RETURN ndf_value_in;

24                                             END IF;

25                             WHEN TOO_MANY_ROWS

26                             THEN

27                                             q$error_manager.raise_unanticipated

28                                                 (text_in   =>  'Multiple rows found for department name'

29                                             ,    name1_in  =>  'DEPARTMENT_NAME'

30                                             ,    value1_in =>  department_name_in

31                                            );

32  END id_for_name;

Второе , новый параметр ndf _ value _ in предоставляет значение, которое будет использоваться, чтобы указать, что данные не найдены, если исключение не распространяется. Возможно, вы захотите просто возвращать NULL , чтобы указать "данные не найдены" (" no data found "), но это значение (или, точнее, отсутствие значения) иногда может быть допустимым значением столбца. Зачем же жестко кодировать его?

Третье , если исключение NO _ DATA _ FOUND возникло, то оно распространяется за пределы функции путем повторного вызова ( RAISE ; в строке 21), только если пользователь запросил такое поведение. Иначе функция возвращает значение индикатора "данные не найдены" (" no data found ").

И последнее, если возникло TOO _ MANY _ ROWS , утилита управления ошибками регистрирует ошибку, включая ID отдела, который вызвал проблему и распространяет исключение за пределы функции необработанным.

Теперь, используя новую версию функции id _ for _ name , Сандра может переписать свою программу загрузки (смотри Листинг 8). Она решила использовать -1 для указания, что отдел не найден. Она также "спрятала" -1 в константу, чтобы код был более понятным. Вся логика приложения размещается в исполняемом разделе, а код стал более ясным и простым для понимания и управления.

Листинг 8: Вызов измененной функции ID_FOR_NAME

PROCEDURE load_from_staging_table

IS

    c_no_such_dept        CONSTANT PLS_INTEGER := -1;

    l_id departments.department_id%TYPE;

BEGIN

    FOR dept_rec IN (SELECT * FROM dept_staging_table)

    LOOP

                   BEGIN

                                  l_id :=

                                                 id_for_name (dept_rec.department_name

                                                                                                                , raise_if_ndf_in => FALSE

                                                                                                                , ndf_value_in         => c_no_such_dept

                                                                                                                );

                                  IF l_id = c_no_such_dept

                                  THEN

                                                 submit_update_request (dept_rec);

                                  ELSE

                                                 submit_add_request (dept_rec);

                                  END IF;

                   END;

    END LOOP;

END load_from_staging_table;

Не надейтесь, что вы сможете удалить все разделы исключений в PL / SQL коде, и он будет хорошо работать, когда никакие исключения не возникают. Существование намеренных и неуместных исключений делает это невозможным.

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

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

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 15.06.2009 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Oracle Database Personal Edition Named User Plus Software Update License & Support
Oracle Database Personal Edition Named User Plus License
Oracle Database Standard Edition 2 Named User Plus License
Oracle Database Standard Edition 2 Processor License
IBM DOMINO COLLABORATION EXPRESS AUTHORIZED USER ANNUAL SW SUBSCRIPTION & SUPPORT RENEWAL
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
СУБД Oracle "с нуля"
Утиль - лучший бесплатный софт для Windows
Каждый день новые драйверы для вашего компьютера!
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100