Про винятки і правилах

Автор: Стівен Фернстайн, член колегії 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 коді, і він буде добре працювати, коли ніякі винятку не виникають. Існування навмисних і недоречних виключень робить це неможливим.


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


Який би підхід ви не вибрали, саме головне – це обговорити ці питання перед початком створення наступного додатки.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*