все SQL-запросы в виде объявлений явных курсоров;
все пользовательские исключения.
Настоятельно рекомендуется все объявления программных элементов такого рода всех программ PL/SQL, реализующих серверную бизнес-логику системы, собрать в одной или нескольких спецификациях пакетов, а не «размазывать» объявления типов, исключений, констант и т. п. по всему коду или переписывать одну и ту же команду SELECT INTO в нескольких местах. Иногда даже создают отдельные спецификации пакетов только для объявлений типов, переменных, исключений и курсоров без объявлений процедур и функций. Для таких спецификаций изначально не планируется создавать тела пакетов.
Ни в коем случае не следует расставлять по всему исходному коду PL/SQL жестко кодируемые литералы. Например, если в коде в сорока местах для вычисления сумм «чистыми» использовать выражения вида (…)*0.87, то когда ставка подоходного налога перестанет быть равной 13%, надо будет найти все сорок мест и заменить 0.87 на новое значение. А самое интересное начнется, если почти везде по коду поменять значение литерала на новое, а где-то забыть и оставить старое. Чтобы не заниматься всем этим, правильно один раз объявить в спецификации пакета константу
g_c_tax_percent INTEGER := 13;
и во всем коде в дальнейшем использовать только ее. Если потребуется внести изменения, то новое число нужно будет указать только в одном месте – в значении константы.
Отношения между спецификацией и телом пакета
Отношения между спецификацией и телом пакета описываются следующим образом:
сначала создается спецификация пакета, затем его тело;
тело пакета не может существовать без спецификации и даже не создастся DDL-командой CREATE PACKAGE BODY с выдачей сообщения об ошибке;
спецификация пакета без тела существовать может, на объявленные в ней глобальные пакетные процедуры и функции можно ссылаться из других программ PL/SQL (ошибка обращения к такому бестелесному пакету возникнет только на этапе выполнения);
при перекомпиляции спецификации пакета автоматически перекомпилируется его тело, при перекомпиляции тела пакета его спецификация не перекомпилируется;
при удалении спецификации пакета автоматически удаляется его тело, при удалении тела пакета с его спецификацией ничего не происходит.
Эти отношения между спецификацией и телом пакета имеют следующие положительные аспекты, которые особенно полезны при большом объеме кода на PL/SQL в крупных проектах:
возможность создания на этапе прототипирования «заглушек» – пакетов без тел, реализация объявленных в их спецификациях процедур и функций будет осуществлена позже;
при перекомпиляции тела пакета с зависящими от его спецификации другими программами на PL/SQL ничего не происходит.
Перегружаемые программы
Часто использующейся возможностью пакетов является поддержка перегружаемых (overloaded) процедур и функций, для которых в пакете существует несколько версий программ с одинаковыми именами, но с различными по составу или типам данных параметрами:
– статус клиента на текущую дату
FUNCTION getClientStatus(p_client_id IN INTEGER) RETURN VARCHAR2
– статус клиента на дату-параметр
FUNCTION getClientStatus(p_client_id IN INTEGER,
p_status_date IN DATE) RETURN VARCHAR2
При вызове перегруженной процедуры или функции по числу и типу передаваемых фактических параметров автоматически определяется требуемая версия процедуры или функции, которая и вызывается. Для приведенных вариантов объявления функции при указании одного фактического параметра вызовется первая версия функции getClientStatus, с двумя фактическими параметрами – вторая. Довольно часто перегружаемые программы вызывают друг друга, например, первую версию функции getClientStatus было бы правильно реализовать с помощью ее же второй версии:
FUNCTION getClientStatus(p_client_id IN INTEGER) IS
BEGIN
RETURN getClientStatus(p_client_id => p_client_id,
p_status_date => SYSDATE);
END;
Поддержка перегружаемых программ позволяет не придумывать различные имена для функций с одинаковой по смыслу функциональностью, но отличающимися типами параметров. Например, вместо того, чтобы помнить названия процедур printPerson, printPassport, … и постоянно добавлять к ним новые подобные процедуры, гораздо проще знать, что все составные типы данных печатаются одной перегруженной процедурой print, имеющей несколько версий.
print(p_person t_person), print(p_passport t_passport), …
Создание пакетов
Спецификацию пакета принято начинать с объявления пользовательских типов данных (подтипов имеющихся в языке скалярных типов, записей PL/SQL, коллекций), потом обычно объявляются курсоры, затем константы и переменные (в том числе и пользовательских типов, объявленных в спецификации выше). После всех этих объявлений помещают заголовки глобальных пакетных процедур и функций.
Читать дальше