IgorSikors.github.io

My lessons Oracle

Follow me on GitHub

Пакеты

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

Для чего нужны пакеты?

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

  • Упрощение сопровождения и расширения приложений. По мере того как все большая часть кодовой базы перемещается в режим сопровождения, качество приложений PL/SQL определяется не только их производительностью, но и простотой сопровождения. С этой точки зрения пакеты играют исключительно важную роль, поскольку они обеспечивают инкапсуляцию кода (в частности, они позволяют скрыть команды SQL за интерфейсом процедур), дают возможность определять константы для литералов и «волшебных» чисел, и группировать логически связанные функции. Пакетный подход к проектированию и реализации сокращает количество потенциальных сбоев в приложениях.
  • Повышение производительности приложений. Во многих ситуациях использование пакетов повышает производительность и эффективность работы приложений. Определение постоянных структур данных уровня пакета позволяет кэшировать статические значения из базы данных. Это дает возможность избежать повторных запросов, а следовательно, значительно ускорить получение результата. Кроме того, подсистема управления памятью Oracle оптимизирована для доступа к откомпилированному коду пакетов.
  • Исправление недостатков приложений или встроенных элементов. Некоторые из существующих программных компонентов Oracle имеют недостатки; в частности, не лучшим образом реализованы важнейшие функции встроенных пакетов UTL_FILE и DBMS_OUTPUT. Мириться с ними не обязательно; можно разработать собственный пакет на базе существующего, исправив как можно больше проблем.
  • Снижение необходимости в перекомпиляции кода. Пакет обычно состоит из двух элементов: спецификации и тела. Внешние программы (не определенные в пакете) могут вызывать только программы, перечисленные в спецификации. Изменение и перекомпиляция тела пакета не отражается на работе этих внешних программ. Снижение необходимости в перекомпиляции кода является важнейшим фактором администрирования больших объемов программного кода приложений.

Демонстрация возможностей пакетов

Пакет состоит из двух частей — спецификации и тела. Спецификация является обязательной частью и определяет, как разработчик может использовать пакет: какие программы можно вызывать, какие курсоры открывать и т. д. Тело пакета — необязательная, но почти всегда присутствующая часть; она содержит код перечисленных в спецификации программ (и возможно, курсоров), а также другие необходимые элементы кода.

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

Рассмотрим следующую спецификацию пакета:

PACKAGE employee_pkg AS
SUBTYPE fullname_t IS VARCHAR2 (200);

FUNCTION fullname (
last_in employees.last_name%TYPE,
first_in employees.first_name%TYPE)
RETURN fullname_t;

FUNCTION fullname (
employee_id_in IN employees.employee_id%TYPE)
RETURN fullname_t;
END employee_pkg;

Фактически здесь перечисляются различные элементы, которые должны использоваться разработчиками.

Основные концепции пакетов

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

  • Сокрытие информации. Сокрытие информации о системе или приложении обычно преследует две цели. Во-первых, возможности человека по работе со сложными системами ограничены. Исследования показали, что у среднего «мозга» при запоминании даже семи (плюс/минус двух) элементов в группе возникают проблемы. Таким образом, пользователь (или разработчик) освобождается от необходимости вникать в ненужные подробности и может сосредоточиться на действительно важных аспектах. Во-вторых, сокрытие информации препятствует доступу к закрытым сведениям. Например, разработчик может вызвать в своем приложении готовую функцию для вычисления некоторого значения, но при этом формула вычислений может быть секретной. Кроме того, в случае изменения формулы все модификации будут вноситься только в одном месте.
  • Общие и приватные элементы. Концепция общих и приватных элементов тесно связана с концепцией сокрытия информации. Общедоступный код определяется в спецификации пакета и доступен любой схеме, обладающей для этого пакета привилегией EXECUTE. Приватный код виден только в пределах пакета. Внешние программы, работающие с пакетом, не видят приватный код и не могут использовать его.

На ранних стадиях развития программы в теле пакета также могут реализоваться в виде «заглушек» с минимальным объемом кода, необходимым для компиляции пакета. Этот прием позволяет сосредоточиться на интерфейсах программы и их взаимных связях.

  • Спецификация пакета. Она содержит определения всех общедоступных элементов пакета, на которые можно ссылаться извне. Спецификация напоминает большой раздел объявлений; она не содержит блоков PL/SQL или исполняемого кода. Из хорошо спроектированной спецификации разработчик может получить всю необходимую для использования пакета информацию и ему никогда не придется заглядывать «за интерфейс» (то есть в тело пакета, содержащее реализацию его компонентов).
  • Тело пакета. Здесь находится весь код, который необходим для реализации элементов, определенных в спецификации пакета. Тело может содержать отсутствующие в спецификации личные элементы, на которые нельзя ссылаться извне пакета, в частности объявления переменных и определения пакетных модулей. Кроме того,в теле пакета может находиться исполняемый (инициализационный) раздел, который выполняется только один раз для инициализации пакета.
  • Инициализация. Концепция инициализации хорошо известна любому программисту, однако в контексте пакетов она имеет особое значение. В данном случае инициализируется не отдельная переменная, а весь пакет путем выполнения кода произвольной сложности. При этом Oracle следит за тем, чтобы пакет инициализировался только один раз за сеанс.
  • Постоянство в течение сеанса. Концепция постоянства (или сохраняемости) тоже хорошо знакома программистам. Когда вы подключаетесь к Oracle и выполняете программу, присваивающую значение переменной уровня пакета (то есть переменной, объявленной в пакете вне содержащихся в нем программ), эта переменная сохраняет значение в течение всего сеанса, даже если выполнение присвоившей его программы завершается. Также существует концепция сеансового постоянства. Если я подключаюсь к базе данных Oracle (создаю сеанс) и выполняю программу, которая присваивает значение пакетной переменной (то есть переменной, объявленной в спецификации или теле пакета, за пределами всех входящих в него программ), то эта переменная продолжает существовать на всем протяжении сеанса и сохраняет свое значение даже при завершении программы, выполнившей присваивание.

Спецификация пакета

Спецификация пакета содержит список всех доступных элементов и предоставляет разработчику информацию, необходимую для использования пакета в приложениях. Ее часто называют программным интерфейсом — API (Application Programming Interface). Чтобы узнать, как применять описанные в спецификации элементы, разработчику не нужно изучать код, находящийся в теле пакета.

При разработке спецификации пакета необходимо руководствоваться следующими правилами:

  • Элементы практически любого типа — числа, исключения, типы, коллекции и т. д. — могут объявляться на уровне пакета (то есть такие элементы не принадлежат конкретным процедурам или функциям этого пакета). Такие данные называются данными уровня пакетов. В общем случае объявлять переменные в спецификациях пакетов не рекомендуется, хотя объявления констант на уровне пакета вполне приемлемы. В пакете (как в спецификации, так и в теле) нельзя объявлять курсорные переменные (типа REF CURSOR), поскольку они не могут сохранять свое значение на протяжении сеанса (о постоянстве данных пакетов рассказано в разделе «Работа с данными пакета» далее в этой главе).
  • В спецификации допускается объявление типов для любых структур данных: коллекций, записей или курсорных переменных.
  • В спецификации можно объявлять процедуры и функции, но в ней должны быть указаны только их заголовки (часто определения процедуры или функции до ключевого слова IS или AS). Заголовок должен завершаться символом «;» (точка с запятой).
  • В спецификацию пакета могут включаться явные курсоры. Они могут быть представлены в одной из двух форм: SQL-запрос либо является частью объявления курсора, либо скрывается в теле пакета (тогда в объявлении присутствует только предложение RETURN). Эта тема подробно рассматривается в разделе «Пакетные курсоры».
  • Если в спецификации пакета объявляются процедуры или функции либо пакетный курсор без запроса, то тело пакета должно включать реализацию этих элементов.
  • Спецификация пакета может содержать условие AUTHID, определяющее, как будут разрешаться ссылки на объекты данных: в соответствии с привилегиями владельца пакета (AUTHID DEFINER) или того, кто его вызывает (AUTHID CURRENT_USER).
  • После команды END в конце спецификации пакета можно разместить необязательную метку, идентифицирующую пакет:
     END my_package;
    

    Для демонстрации этих правил рассмотрим простую спецификацию пакета: ``` PACKAGE favorites_pkg AUTHID CURRENT_USER IS /* или AS */ – Две константы: вместо малопонятных значений – используются информативные имена.

c_chocolate CONSTANT PLS_INTEGER := 16; c_strawberry CONSTANT PLS_INTEGER := 29;

– Объявление типа вложенной таблицы TYPE codes_nt IS TABLE OF INTEGER;

– Вложенная таблица, объявленная на основе типа. my_favorites codes_nt;

– Курсорная переменная, возвращающая информацию из favorites. TYPE fav_info_rct IS REF CURSOR RETURN favorites%ROWTYPE;

– Процедура, принимающая список значений объявленного – выше типа codes_nt и выводящая соответствующую – информацию из таблицы.. PROCEDURE show_favorites (list_in IN codes_nt); – Функция, возвращающая всю информацию из таблицы – favorites о самом популярном элементе. FUNCTION most_popular RETURN fav_info_rct;

END favorites_pkg; – Закрывающая метка пакета


### Тело пакета
Тело пакета содержит весь код, необходимый для реализации спецификации пакета. Оно не является стопроцентно необходимым; примеры спецификаций пакетов без тела приведены в разделе «Когда используются пакеты». Тело пакета необходимо в том случае, если истинны хотя бы некоторые из следующих условий:
- Спецификация пакета содержит объявление курсора с секцией RETURN. В этом случае команда SELECT должна быть указана в теле пакета.
- Спецификация пакета содержит объявление процедуры или функции. В этом случае реализация модуля должна быть завершена в теле пакета.
- При инициализации пакета должен выполняться код, указанный в инициализационном разделе. Спецификация пакета не поддерживает исполняемый раздел (исполняемые команды в блоке BEGIN-END); эти команды могут находиться только в теле пакета. Со структурной точки зрения тело пакета очень похоже на определение процедуры. Несколько правил, специфических для тел пакетов:
- Тело пакета может содержать раздел объявлений, исполняемый раздел и раздел исключения. Раздел объявлений содержит полную реализацию всех курсоров и программ, определяемых в спецификации, а также определение всех приватных элементов (не указанных в спецификации). Раздел объявлений может быть пустым — при условии, что в теле пакета присутствует инициализационный раздел.
- Исполняемый раздел пакета также называется инициализационным разделом; он содержит дополнительный код, выполняемый при инициализации пакета в сеансе.
- В разделе исключений обрабатываются все исключения, инициированные в инициализационном разделе. Раздел исключений может располагаться в конце тела пакета только в том случае, если вы определили инициализационный раздел.
- Тело пакета может иметь следующую структуру: только раздел объявлений; только
исполняемый раздел; исполняемый раздел и раздел исключений; раздел объявлений, исполняемый раздел и раздел исключений.
- Секция AUTHID не может входить в тело пакета; она должна размещаться в спецификации пакета. Все, что объявлено в спецификации, может использоваться в теле пакета.
- Для тела и спецификации пакета действуют одни правила и ограничения объявления
структур данных — например, невозможность объявления курсорных переменных.
- За командой END тела пакета может следовать необязательная метка с именем пакета: END my_package;

Ниже приведена реализация тела favorites_pkg:

/* Файл в Сети: favorites.sql */ PACKAGE BODY favorites_pkg IS – Приватная переменная g_most_popular PLS_INTEGER := c_strawberry; – Реализация функции FUNCTION most_popular RETURN fav_info_rct IS retval fav_info_rct; null_cv fav_info_rct; BEGIN OPEN retval FOR SELECT * FROM favorites WHERE code = g_most_popular; RETURN retval; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN null_cv; END most_popular; – Реализация процедуры PROCEDURE show_favorites (list_in IN codes_nt) IS BEGIN FOR indx IN list_in.FIRST .. list_in.LAST LOOP DBMS_OUTPUT.PUT_LINE (list_in (indx)); END LOOP; END show_favorites; END favorites_pkg; – Метка конца пакета

 
### Инициализация пакетов
Пакет может содержать структуры данных, сохраняющиеся на протяжении всего сеанса (см. раздел «Работа с данными пакета»). Когда в ходе сеанса впервые происходит обращение к пакету (вызывается объявленная в нем программа, считывается или записывается значение переменной либо используется объявленный в пакете тип), Oracle инициализирует его, выполняя следующие действия:
- Создание экземпляров данных уровня пакетов (значения переменных и констант).
- Присваивание переменным и константам значений по умолчанию, указанных в объявлениях.
- Выполнение блока кода, содержащегося в инициализационном разделе.

Oracle выполняет все эти действия только один раз за сеанс и только тогда, когда возникнет непосредственная необходимость в этой информации.

Инициализационный раздел пакета составляют все операторы, находящиеся между ключевым словом BEGIN (вне определений процедур и функций) и ключевым словом END, завершающим тело пакета. Например, инициализационный раздел пакета favorites_pkg
может выглядеть так:

PACKAGE BODY favorites_pkg IS g_most_popular PLS_INTEGER; PROCEDURE show_favorites (list_in IN codes_nt) … END; FUNCTION most_popular RETURN fav_info_rct … END; PROCEDURE analyze_favorites (year_in IN INTEGER) … END; – Инициализационный раздел BEGIN g_most_popular := c_chocolate; – Функция EXTRACT используется для извлечения года из значения, – возвращаемого функцией SYSDATE analyze_favorites (EXTRACT (YEAR FROM SYSDATE)); END favorites_pkg; ```

## Вопросы для самопроверки

  1. Что такое пакет?
  2. Для чего нужны пакеты?
  3. Назовите основные концепции пакетов
  4. Из каких элементов состоит пакет?
  5. Как инициализируются пакеты?

## Задачи

  1. Создайте пакет только со спецификацией
  2. Добавьте тело пакета к спецификации
  3. Напишите одну любую процедуру и функцию в пакете на ваш выбор

Предыдущий урок Вернуться на главную и пройти тест