Создание пакета в oracle
A package is a schema object that groups logically related PL/SQL types, variables, constants, subprograms, cursors, and exceptions. A package is compiled and stored in the database, where many applications can share its contents.
A package always has a specification , which declares the public items that can be referenced from outside the package.
If the public items include cursors or subprograms, then the package must also have a body . The body must define queries for public cursors and code for public subprograms. The body can also declare and define private items that cannot be referenced from outside the package, but are necessary for the internal workings of the package. Finally, the body can have an initialization part , whose statements initialize variables and do other one-time setup steps, and an exception-handling part. You can change the body without changing the specification or the references to the public items; therefore, you can think of the package body as a black box.
In either the package specification or package body, you can map a package subprogram to an external Java or C subprogram by using a call specification , which maps the external subprogram name, parameter types, and return type to their SQL counterparts.
The AUTHID clause of the package specification determines whether the subprograms and cursors in the package run with the privileges of their definer (the default) or invoker, and whether their unqualified references to schema objects are resolved in the schema of the definer or invoker.
The ACCESSIBLE BY clause of the package specification lets you specify a white list of PL/SQL units that can access the package. You use this clause in situations like these:
You implement a PL/SQL application as several packages—one package that provides the application programming interface (API) and helper packages to do the work. You want clients to have access to the API, but not to the helper packages. Therefore, you omit the ACCESSIBLE BY clause from the API package specification and include it in each helper package specification, where you specify that only the API package can access the helper package.
You create a utility package to provide services to some, but not all, PL/SQL units in the same schema. To restrict use of the package to the intended units, you list them in the ACCESSIBLE BY clause in the package specification.
"Package Specification" for more information about the package specification
The CREATE PACKAGE statement creates or replaces the specification for a stored package , which is an encapsulated collection of related procedures, functions, and other program objects stored as a unit in the database.
The package specification declares these objects. The package body , specified subsequently, defines these objects.
To create or replace a package in your schema, you must have the CREATE PROCEDURE system privilege. To create or replace a package in another user's schema, you must have the CREATE ANY PROCEDURE system privilege.
To embed a CREATE PACKAGE statement inside an Oracle database precompiler program, you must terminate the statement with the keyword END-EXEC followed by the embedded SQL statement terminator for the specific language.
Re-creates the package if it exists, and recompiles it.
Users who were granted privileges on the package before it was redefined can still access the package without being regranted the privileges.
If any function-based indexes depend on the package, then the database marks the indexes DISABLED .
Specifies whether the package is an editioned or noneditioned object if editioning is enabled for the schema object type PACKAGE in schema . Default: EDITIONABLE . For information about editioned and noneditioned objects, see Oracle Database Development Guide .
Name of the schema containing the package. Default: your schema.
A package stored in the database. For naming conventions, see "Identifiers" .
Defines every type in the package and declares every cursor and subprogram in the package. Except for polymorphic table functions, every declaration must have a corresponding definition in the package body. The headings of corresponding declarations and definitions must match word for word, except for white space. Package polymorphic table function must be declared in the same package as their implementation package.
Restriction on package_item_list
PRAGMA AUTONOMOUS_TRANSACTION cannot appear here.
Example 14-19 Creating the Specification for the emp_mgmt Package
This statement creates the specification of the emp_mgmt package.
The specification for the emp_mgmt package declares these public program objects:
The functions hire and create_dept
The procedures remove_emp , remove_dept , increase_sal , and increase_comm
The exceptions no_comm and no_sal
All of these objects are available to users who have access to the package. After creating the package, you can develop applications that invoke any of these public procedures or functions or raise any of the public exceptions of the package.
Before you can invoke this package's procedures and functions, you must define these procedures and functions in the package body. For an example of a CREATE PACKAGE BODY statement that creates the body of the emp_mgmt package, see "CREATE PACKAGE BODY Statement" .
В Oracle PL/SQL набор элементов: процедур, функций, определения типов; объявления переменных, констант можно объединить в пакет. После написания пакет PL/SQL компилируется, а затем сохраняется в базе данных Oracle, где его содержимое может использоваться многими приложениями.
Синтаксис
Синтаксис CREATE PACKAGE в Oracle PL/SQL:
CREATE [OR REPLACE] PACKAGE package_name
[AUTHID ]
[PRAGMA SERIALLY_REUSABLE;]
[collection_type_definition . ]
[record_type_definition . ]
[subtype_definition . ]
[collection_declaration . ]
[constant_declaration . ]
[exception_declaration . ]
[object_declaration . ]
[record_declaration . ]
[variable_declaration . ]
[cursor_spec . ]
[function_spec . ]
[procedure_spec . ]
[call_spec . ]
[PRAGMA RESTRICT_REFERENCES(assertions) . ]
END [package_name];
[CREATE [OR REPLACE] PACKAGE BODY package_name
[PRAGMA SERIALLY_REUSABLE;]
[collection_type_definition . ]
[record_type_definition . ]
[subtype_definition . ]
[collection_declaration . ]
[constant_declaration . ]
[exception_declaration . ]
[object_declaration . ]
[record_declaration . ]
[variable_declaration . ]
[cursor_body . ]
[function_spec . ]
[procedure_spec . ]
[call_spec . ]
[BEGIN
sequence_of_statements]
END [package_name];]
collection_type_definition - определение типа коллекции
record_type_definition - определение типа записи
subtype_definition - определение подтипа
collection_declaration - объявление коллекции
constant_declaration - объявление константы
exception_declaration - объявление исключения
object_declaration - объявление объекта
record_declaration - объявление записи
variable_declaration - объявление переменной
cursor_spec - спецификация курсора
function_spec - спецификация функции
procedure_spec - спецификация процедуры
call_spec - спецификация вызова
Спецификация пакета содержит публичные объявления, которые видны вашему приложению. Вы должны объявить подпрограммы в конце спецификации после всех других элементов (кроме прагм, которые вызывают конкретную функцию; такие прагмы должны следовать спецификации функции).
Тело пакета содержит детали реализации и приватные объявления, которые скрыты от вашего приложения. За декларативной частью тела пакета следует необязательная часть инициализации, которая обычно содержит операторы, которые инициализируют переменные пакета.
Демонстрация возможностей пакетов PL/SQL
Пакет состоит из двух частей — спецификации и тела. Спецификация является обязательной частью и определяет, как разработчик может использовать пакет: какие программы можно вызывать, какие курсоры открывать и т. д. Тело пакета — необязательная, но почти всегда присутствующая часть; она содержит код перечисленных в спецификации программ (и возможно, курсоров), а также другие необходимые элементы кода. Предположим, нам нужна программа для получения полного имени сотрудника, которое хранится в базе данных в виде двух отдельных элементов: фамилии и имени. На первый взгляд кажется, что задача решается просто:
Однако этот вроде бы тривиальный код обладает рядом скрытых недостатков:
- Длина переменной l_fullname жестко закодирована. Поскольку полное имя — производное значение, которое строится конкатенацией содержимого двух столбцов, лучше так не делать. Если длина столбцов last_name и/или first_name будет увеличена, код процедуры придется изменять.
- Жестко закодировано правило составления полного имени. Чем это плохо? Тем, что если через какое-то время пользователь захочет получить полное имя в формате «ИМЯ ФАМИЛИЯ», вам придется производить замену во многих местах кода.
- Наконец, этот очень распространенный запрос может встречаться в нескольких местах приложения. Дублирование кода SQL затрудняет сопровождение приложения и его оптимизацию.
Приложения должны строиться таким образом, чтобы избежать жесткого кодирования подобных элементов. Определение типа данных для полного имени, представление, запрос к базе данных и т. п. должны кодироваться один раз в строго определенном месте и быть доступны из любой точки приложения. Таким местом и является пакет. Рассмотрим следующую спецификацию пакета:
Фактически здесь перечисляются различные элементы, которые должны использоваться разработчиками. Важнейшие элементы кода представлены в следующей таблице.
Строки | Описание |
3 | Объявление нового типа данных fullname_t. В этой версии его максимальная длина составляет 200 символов, но впоследствии ее будет легко изменить |
5-8 | Объявление функции fullname , которая строит полное имя по фамилии и имени. Обратите внимание: способ построения полного имени в спецификации пакета не указан |
10-13 | Объявление второй функции с тем же именем fullname ; новая версия получает первичный ключ таблицы и возвращает соответствующее ему полное имя. Это типичный пример перегрузки, о которой говорилось в этой статье |
Но прежде чем рассматривать реализацию пакета, давайте перепишем исходный блок кода таким образом, чтобы в нем использовались элементы пакета (обратите внимание на точечный синтаксис, аналогичный синтаксису таблица.столбец):
Переменная l_name объявляется с новым типом данных, а для присваивания ей нужного значения вызывается соответствующая функция этого же пакета. Таким образом, формула построения полного имени и SQL-запрос вынесены из кода приложения в специальный «контейнер» для всей функциональности, относящейся к обработке данных о сотрудниках. Код стал проще и лаконичнее. Если потребуется изменить формулу построения полного имени или увеличить размер его типа данных, достаточно внести соответствующие изменения в спецификацию или тело пакета и перекомпилировать его код.
Реализация employee_pkg выглядит так:
В следующей таблице перечислены важные элементы этого кода.
Строки | Описание |
3-11 | Функция-«обертка» для определения формата полного имени «ФАМИЛИЯ, ИМЯ» |
13-27 | Типичный запрос на выборку одной строки, выполняемый с помощью неявного курсора |
18 | Вызов функции fullname, возвращающей комбинацию двух компонентов имени |
Если теперь пользователь потребует изменить формат представления полного имени, нам не придется искать по всему приложению все вхождения || ', ' || — для установки нового формата достаточно модифицировать в пакете employee_pkg функцию fullname .
Основные концепции пакетов
Прежде чем переходить к подробному изучению синтаксиса и структуры пакетов, следует изучить некоторые концепции пакетов:
- Сокрытие информации. Сокрытие информации о системе или приложении обычно преследует две цели. Во-первых, возможности человека по работе со сложными системами ограничены. Исследования показали, что у среднего «мозга» при запоминании даже семи (плюс/минус двух) элементов в группе возникают проблемы. Таким образом, пользователь (или разработчик) освобождается от необходимости вникать в ненужные подробности и может сосредоточиться на действительно важных аспектах. Во-вторых, сокрытие информации препятствует доступу к закрытым сведениям. Например, разработчик может вызвать в своем приложении готовую функцию для вычисления некоторого значения, но при этом формула вычислений может быть секретной. Кроме того, в случае изменения формулы все модификации будут вноситься только в одном месте.
- Общие и приватные элементы. Концепция общих и приватных элементов тесно связана с концепцией сокрытия информации. Общедоступный код определяется в спецификации пакета и доступен любой схеме, обладающей для этого пакета привилегией EXECUTE . Приватный код виден только в пределах пакета. Внешние программы, работающие с пакетом, не видят приватный код и не могут использовать его.
Приступая к разработке пакета, вы решаете, какие из его элементов будут общими, а какие — приватными. Кроме того, тело пакета можно скрыть от других схем/разработчиков. В таком случае пакет используется для сокрытия деталей реализации программ. Это особенно полезно для изоляции переменных компонентов приложения — фрагментов кода, зависящих от платформы, часто меняющихся структур данных и временных обходных решений.
На ранних стадиях развития программы в теле пакета также могут реализоваться в виде «заглушек» с минимальным объемом кода, необходимым для компиляции пакета. Этот прием позволяет сосредоточиться на интерфейсах программы и их взаимных связях.
- Спецификация пакета. Она содержит определения всех общедоступных элементов пакета, на которые можно ссылаться извне. Спецификация напоминает большой раздел объявлений; она не содержит блоков PL/SQL или исполняемого кода. Из хорошо спроектированной спецификации разработчик может получить всю необходимую для использования пакета информацию и ему никогда не придется заглядывать «за интерфейс» (то есть в тело пакета, содержащее реализацию его компонентов).
- Тело пакета. Здесь находится весь код, который необходим для реализации элементов, определенных в спецификации пакета. Тело может содержать отсутствующие в спецификации личные элементы, на которые нельзя ссылаться извне пакета, в частности объявления переменных и определения пакетных модулей. Кроме того, в теле пакета может находиться исполняемый (инициализационный) раздел, который выполняется только один раз для инициализации пакета.
- Инициализация. Концепция инициализации хорошо известна любому программисту, однако в контексте пакетов она имеет особое значение. В данном случае инициализируется не отдельная переменная, а весь пакет путем выполнения кода произвольной сложности. При этом Oracle следит за тем, чтобы пакет инициализировался только один раз за сеанс.
- Постоянство в течение сеанса. Концепция постоянства (или сохраняемости) тоже хорошо знакома программистам. Когда вы подключаетесь к Oracle и выполняете программу, присваивающую значение переменной уровня пакета (то есть переменной, объявленной в пакете вне содержащихся в нем программ), эта переменная сохраняет значение в течение всего сеанса, даже если выполнение присвоившей его программы завершается.
Также существует концепция сеансового постоянства. Если я подключаюсь к базе данных Oracle (создаю сеанс) и выполняю программу, которая присваивает значение пакетной переменной (то есть переменной, объявленной в спецификации или теле пакета, за пределами всех входящих в него программ), то эта переменная продолжает существовать на всем протяжении сеанса и сохраняет свое значение даже при завершении программы, выполнившей присваивание.
Именно пакеты обеспечивают поддержку структур данных с сеансовым постоянством в языке PL/SQL.
Для чего нужны пакеты в PL/SQL?
Пакеты — очень важная составная часть языка PL/SQL, краеугольный камень любого сложного проекта. Чтобы это понять, необходимо рассмотреть основные преимущества пакетов:
- Упрощение сопровождения и расширения приложений. По мере того как все большая часть кодовой базы перемещается в режим сопровождения, качество приложений PL/SQL определяется не только их производительностью, но и простотой сопровождения. С этой точки зрения пакеты играют исключительно важную роль, поскольку они обеспечивают инкапсуляцию кода (в частности, они позволяют скрыть команды SQL за интерфейсом процедур), дают возможность определять константы для литералов и «волшебных» чисел, и группировать логически связанные функции. Пакетный подход к проектированию и реализации сокращает количество потенциальных сбоев в приложениях.
- Повышение производительности приложений. Во многих ситуациях использование пакетов повышает производительность и эффективность работы приложений. Определение постоянных структур данных уровня пакета позволяет кэшировать статические значения из базы данных. Это дает возможность избежать повторных запросов, а следовательно, значительно ускорить получение результата. Кроме того, подсистема управления памятью Oracle оптимизирована для доступа к откомпилированному коду пакетов.
- Исправление недостатков приложений или встроенных элементов. Некоторые из существующих программных компонентов Oracle имеют недостатки; в частности, не лучшим образом реализованы важнейшие функции встроенных пакетов UTL_FILE и DBMS_OUTPUT . Мириться с ними не обязательно; можно разработать собственный пакет на базе существующего, исправив как можно больше проблем. Например, сценарий do.pkg, предоставляет замену для встроенной функции DBMS_OUTPUT.PUT_LINE с добавлением перегрузки для типа XMLType. Подобного результата можно достичь и с помощью отдельных функций и процедур PL/SQL, но решение с пакетами более предпочтительно.
- Снижение необходимости в перекомпиляции кода. Пакет обычно состоит из двух элементов: спецификации и тела. Внешние программы (не определенные в пакете) могут вызывать только программы, перечисленные в спецификации. Изменение и перекомпиляция тела пакета не отражается на работе этих внешних программ. Снижение необходимости в перекомпиляции кода является важнейшим фактором администрирования больших объемов программного кода приложений. Концепция пакетов очень проста. Единственная сложность заключается в том, чтобы научиться эффективно применять в приложениях их богатые возможности. В этой статье мы начнем с рассмотрения простого пакета; вы увидите, что основные преимущества пакетов проявляются даже в тривиальном коде. Затем будет рассмотрен специальный синтаксис, используемый при определении пакетов.
Прежде чем приступать к рассмотрению преимуществ пакетов и описанию синтаксиса их определения, необходимо сделать одно важное замечание. Всегда стройте приложение на основе пакетов; избегайте отдельных процедур и функций. Даже если вам сейчас кажется, что для реализации определенной возможности достаточно одной процедуры или функции, в будущем к ней почти наверняка добавятся еще несколько. Когда вы поймете, что их лучше объединить в пакет, придется искать все вызовы процедур и функций и добавлять к ним префикс с именем пакета. Используйте пакеты с самого начала, избавьте себя от будущих проблем!
Спецификация пакета PL/SQL
Спецификация пакета содержит список всех доступных элементов и предоставляет разработчику информацию, необходимую для использования пакета в приложениях. Ее часто называют программным интерфейсом — API (Application Programming Interface). Чтобы узнать, как применять описанные в спецификации элементы, разработчику не нужно изучать код, находящийся в теле пакета.
При разработке спецификации пакета необходимо руководствоваться следующими правилами:
- Элементы практически любого типа — числа, исключения, типы, коллекции и т. д. — могут объявляться на уровне пакета (то есть такие элементы не принадлежат конкретным процедурам или функциям этого пакета). Такие данные называются данными уровня пакетов. В общем случае объявлять переменные в спецификациях пакетов не рекомендуется, хотя объявления констант на уровне пакета вполне приемлемы. В пакете (как в спецификации, так и в теле) нельзя объявлять курсорные переменные (типа REF CURSOR ), поскольку они не могут сохранять свое значение на протяжении сеанса.
- В спецификации допускается объявление типов для любых структур данных: коллекций, записей или курсорных переменных.
- В спецификации можно объявлять процедуры и функции, но в ней должны быть указаны только их заголовки (часто определения процедуры или функции до ключевого слова IS или AS). Заголовок должен завершаться символом « ; » (точка с запятой).
- В спецификацию пакета могут включаться явные курсоры. Они могут быть представлены в одной из двух форм: SQL-запрос либо является частью объявления курсора, либо скрывается в теле пакета (тогда в объявлении присутствует только предложение RETURN ). Эта тема подробно рассматривается в разделе «Пакетные курсоры».
- Если в спецификации пакета объявляются процедуры или функции либо пакетный курсор без запроса, то тело пакета должно включать реализацию этих элементов.
- Спецификация пакета может содержать условие AUTHID , определяющее, как будут разрешаться ссылки на объекты данных: в соответствии с привилегиями владельца пакета ( AUTHID DEFINER ) или того, кто его вызывает ( AUTHID CURRENTJJSER ).
- После команды END в конце спецификации пакета можно разместить необязательную метку, идентифицирующую пакет:
Для демонстрации этих правил рассмотрим простую спецификацию пакета:
Как видите, пакет имеет почти такую же структуру спецификации, как раздел объявлений блока PL/SQL. Единственное отличие заключается в том, что спецификация не может содержать кода реализации.
Графическое представление приватности
Различия между общедоступными и приватными элементами пакета дают разработчикам PL/SQL беспрецедентные средства управления структурами данных и программами. Грэди Буч предложил визуальное средство описания этих аспектов пакета (которое было вполне естественно названо диаграммой Буча).
Взгляните на рис. 1. Обратите внимание на надписи «Внутренняя часть» и «Внешняя часть». Первая часть содержит тело пакета (внутренняя реализация пакета), а вторая — все программы, написанные вами и не являющиеся частью пакета (внешние программы).
Рис. 1. Диаграмма Буча с общедоступными и приватными элементами пакета
Структура пакета PL/SQL выглядит очень просто, но эта простота обманчива. Хотя синтаксис и правила построения пакетов вы изучите очень быстро, полное понимание нюансов реализации придет далеко не сразу. В этом разделе рассматриваются правила построения пакетов, а далее рассказывается, в каких ситуациях пакеты особенно эффективны.
Чтобы создать пакет, необходимо написать его спецификацию и почти всегда — тело. При этом нужно решить, какие элементы пакета будут указаны в спецификации, а какие скрыты в теле. В пакет также можно включить блок кода, который база данных Oracle будет выполнять при инициализации пакета.
Что такое пакет Oracle PL/SQL?
Пакет Oracle PL/SQL - это объект схемы, который группирует логически связанные типы, элементы и подпрограммы. Пакеты обычно состоят из двух частей: спецификации и тела, хотя иногда тело не нужно. Спецификация - это интерфейс для ваших приложений.
В спицификации пакета объявляются типы, переменные, константы, исключения, курсоры и подпрограммы, доступные для использования.
Тело пакета полностью определяет курсоры и подпрограммы и реализует спецификацию.
Как показано на рисунке, вы можете думать о спецификации как о рабочем интерфейсе, а о теле - как о «черном ящике». Вы можете отлаживать, улучшать или изменять тело пакета без изменения интерфейса (спецификации) пакета.
Для создания пакетов используйте оператор CREATE PACKAGE.
Тело пакета
Тело пакета содержит весь код, необходимый для реализации спецификации пакета. Оно не является стопроцентно необходимым; примеры спецификаций пакетов без тела приведены в разделе «Когда используются пакеты». Тело пакета необходимо в том случае, если истинны хотя бы некоторые из следующих условий:
- Спецификация пакета содержит объявление курсора с секцией RETURN . В этом случае команда SELECT должна быть указана в теле пакета.
- Спецификация пакета содержит объявление процедуры или функции. В этом случае реализация модуля должна быть завершена в теле пакета.
- При инициализации пакета должен выполняться код, указанный в инициализационном разделе. Спецификация пакета не поддерживает исполняемый раздел (исполняемые команды в блоке BEGIN-END ); эти команды могут находиться только в теле пакета.
Со структурной точки зрения тело пакета очень похоже на определение процедуры. Несколько правил, специфических для тел пакетов:
- Тело пакета может содержать раздел объявлений, исполняемый раздел и раздел исключения. Раздел объявлений содержит полную реализацию всех курсоров и программ, определяемых в спецификации, а также определение всех приватных элементов (не указанных в спецификации). Раздел объявлений может быть пустым — при условии, что в теле пакета присутствует инициализационный раздел.
- Исполняемый раздел пакета также называется инициализационным разделом; он содержит дополнительный код, выполняемый при инициализации пакета в сеансе. Эта тема будет рассмотрена в следующем разделе.
- В разделе исключений обрабатываются все исключения, инициированные в инициализационном разделе. Раздел исключений может располагаться в конце тела пакета только в том случае, если вы определили инициализационный раздел.
- Тело пакета может иметь следующую структуру: только раздел объявлений; только исполняемый раздел; исполняемый раздел и раздел исключений; раздел объявлений, исполняемый раздел и раздел исключений.
- Секция AUTHID не может входить в тело пакета; она должна размещаться в спецификации пакета. Все, что объявлено в спецификации, может использоваться в теле пакета.
- Для тела и спецификации пакета действуют одни правила и ограничения объявления структур данных — например, невозможность объявления курсорных переменных.
- За командой END тела пакета может следовать необязательная метка с именем пакета: END my_package ;
Ниже приведена моя реализация тела favorites_pkg :
Другие примеры тел пакетов приведены в разделе «Когда используются пакеты».
Пример пакета Oracle PL/SQL
В приведенном ниже примере, вы определяете тип запись, курсор и две процедуры по трудоустройству. Обратите внимание, что процедура hire_employee использует последовательность базы данных empno_seq и функцию SYSDATE для вставки нового номера сотрудника и дату приема на работу соответственно.
Пакет PL/SQL представляет собой сгруппированный по определенным правилам именованный набор элементов кода PL/SQL . Он обеспечивает логическую структуру для организации программ и других элементов PL/SQL : курсоров, типов данных и переменных. Пакеты обладают очень важными функциональными возможностями, включая возможность сокрытия логики и данных, а также определения глобальных данных, существующих в течение сеанса.
Инициализация пакетов
Пакет может содержать структуры данных, сохраняющиеся на протяжении всего сеанса (см. раздел «Работа с данными пакета»). Когда в ходе сеанса впервые происходит обращение к пакету (вызывается объявленная в нем программа, считывается или записывается значение переменной либо используется объявленный в пакете тип), Oracle инициализирует его, выполняя следующие действия:
- Создание экземпляров данных уровня пакетов (значения переменных и констант).
- Присваивание переменным и константам значений по умолчанию, указанных в объявлениях.
- Выполнение блока кода, содержащегося в инициализационном разделе.
Oracle выполняет все эти действия только один раз за сеанс и только тогда, когда возникнет непосредственная необходимость в этой информации.
Пакет может быть повторно инициализирован в ходе сеанса, если он был перекомпилирован с момента последнего использования или был выполнен сброс состояния всего сеанса, на что указывает ошибка ORA-04068.
Инициализационный раздел пакета составляют все операторы, находящиеся между ключевым словом BEGIN (вне определений процедур и функций) и ключевым словом END , завершающим тело пакета. Например, инициализационный раздел пакета favorites_pkg может выглядеть так:
PL/SQL автоматически определяет, когда должен выполняться код инициализацион- ного раздела. Это означает, что нет необходимости вызывать его явно и можно быть уверенными в том, что он будет выполнен только один раз. Когда следует использовать инициализационный раздел? Ниже описаны некоторые возможные причины.
Выполнение сложной логики инициализации
Конечно, значения по умолчанию могут присваиваться пакетным данным прямо в команде объявления. Тем не менее у этого подхода есть несколько потенциальных недостатков:
- Логика, необходимая для назначения значений по умолчанию, может быть слишком сложной для использования в конструкциях значений по умолчанию.
- Если при присваивании значения по умолчанию инициируется исключение, оно не может быть перехвачено в границах пакета; это исключение передается наружу необработанным. Эта тема более подробно рассматривается далее в разделе «Ошибки при инициализации».
Инициализация данных в инициализационном разделе обладает рядом преимуществ перед присваиванием значений по умолчанию. В частности, в исполняемом разделе вы обладаете полной гибкостью в определении, структуре и документировании ваших действий, а при возникновении исключения вы можете обработать его в разделе исключений инициализационного раздела.
Кэширование статической сеансовой информации
Другая причина для включения инициализационного раздела в пакет — кэширование статической информации, то есть остающейся неизменной на протяжении сеанса. Если значения данных не изменяются, зачем мириться с лишними затратами на запросы или повторное вычисление этих данных?
Кроме того, если вы хотите принять меры к тому, чтобы информация читалась в сеансе только один раз, инициализационный раздел становится идеальным автоматизированным решением.
При работе с кэшированными пакетными данными приходится учитывать важный компромисс между затратами памяти и вычислительных мощностей. Кэшируя данные в пакетных переменных, можно улучшить время выборки данных. Для этого данные размещаются «ближе» к пользователю, в области PGA каждого сеанса. При 1000 сеансах в системе существует 1000 копий кэшированных данных. Кэширование снижает нагрузку на процессор, но увеличивает затраты памяти — причем иногда весьма значительно.
За дополнительной информацией по этой теме обращайтесь к разделу «Кэширование статических данных сеанса для ускорения работы приложения».
Предотвращение побочных эффектов при инициализации
Избегайте присваивания значений глобальных данных в других пакетах (и вообще любых значений в других пакетах, если уж на то пошло). Эта защитная мера поможет предотвратить хаос при выполнении кода и потенциальную путаницу у программистов, занимающихся сопровождением. Код инициализационного раздела должен быть сконцентрирован на текущем пакете. Помните, что этот код выполняется тогда, когда ваше приложение в первый раз пытается использовать элемент пакета. Пользователи не должны сидеть сложа руки, пока пакет выполняет высокозатратные вычисления, которые можно вынести в другие пакеты или триггеры приложения. Пример кода, которого следует избегать:
Если ваши требования к инициализации отличны от представленных нами, рассмотрите альтернативу для инициализационного раздела — например, сгруппируйте стартовые команды в процедуре приложения. Присвойте процедуре содержательное имя (например, init_environment); затем в нужной точке процесса инициализации вызовите процедуру init_environment для настройки сеанса.
Ошибки при инициализации
Инициализация пакета проходит в несколько этапов: объявление данных, присваивание значений по умолчанию, выполнение инициализационного раздела (если он присутствует). А если произойдет ошибка, приводящая к сбою процесса инициализации? Оказывается, даже если пакет не может завершить свои действия по инициализации, база данных помечает пакет как инициализированный и не пытается снова выполнять стартовый код в этом сеансе. Чтобы убедиться в этом, рассмотрим следующий пакет:
Допустим, я подключаюсь к SQL*Plus и пытаюсь выполнить функцию valerr.get (первый раз в этом сеансе). Вот что я увижу:
Иначе говоря, попытка объявления переменной v для присваивания значения «ABC» приводит к исключению VALUE_ERROR . Раздел исключений в конце пакета не перехватывает ошибку; он может перехватывать только те ошибки, которые инициируются в самом инициализационном разделе. Таким образом, исключение остается необработанным. Однако следует заметить, что при повторном вызове этой функции в сеансе ошибка уже не выдается:
Как интересно! Строка «Before I show you v. » вообще не выводится; более того, эта команда не выполняется. Ошибка происходит при первом вызове пакетной функции, но не при втором и всех последующих вызовах. Перед нами одна из классических «невоспроизводимых ошибок», а в мире PL/SQL это типичная причина подобных проблем: сбой в ходе инициализации пакета.
Подобные ошибки усложняют диагностику. Чтобы снизить риск таких ошибок и упростить их обнаружение, лучше всего переместить присваивание значений по умолчанию в инициализационный раздел, чтобы раздел исключений мог корректно обрабатывать ошибки и сообщать об их вероятных причинах:
Вы даже можете стандартизировать структуру пакетов и потребовать обязательного включения процедуры инициализации, чтобы разработчики группы не забывали об этой проблеме:
Читайте также: