Что такое пакет в oracle
10 PL/SQL Packages
This chapter explains how to bundle related PL/SQL code and data into a package, whose contents are available to many applications.
Oracle Database PL/SQL Packages and Types Reference for information about the many product-specific packages that Oracle Database supplies
«DROP PACKAGE Statement», which drops a stored package from the database
What is a Package?
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. You can think of a package as an application.
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. For more information, see «Invoker’s Rights and Definer’s Rights (AUTHID Property)».
Reasons to Use Packages
Packages support the development and maintenance of reliable, reusable code with the following features:
Packages let you encapsulate logically related types, variables, constants, subprograms, cursors, and exceptions in named PL/SQL modules. You can make each package easy to understand, and make the interfaces between packages simple, clear, and well defined. This practice aids application development.
Easier Application Design
When designing an application, all you need initially is the interface information in the package specifications. You can code and compile specifications without their bodies. Next, you can compile standalone subprograms that reference the packages. You need not fully define the package bodies until you are ready to complete the application.
Packages let you share your interface information in the package specification, and hide the implementation details in the package body. Hiding the implementation details in the body has these advantages:
You can change the implementation details without affecting the application interface.
Application users cannot develop code that depends on implementation details that you might want to change.
Package public variables and cursors can persist for the life of a session. They can be shared by all subprograms that run in the environment. They let you maintain data across transactions without storing it in the database. (For the situations in which package public variables and cursors do not persist for the life of a session, see «Package State».)
The first time you invoke a package subprogram, Oracle Database loads the whole package into memory. Subsequent invocations of other subprograms in same the package require no disk I/O.
Packages prevent cascading dependencies and unnecessary recompiling. For example, if you change the body of a package function, Oracle Database does not recompile other subprograms that invoke the function, because these subprograms depend only on the parameters and return value that are declared in the specification.
You cannot reference host variables from inside a package.
Package Specification
Each public item declaration has all information that you need to use the item. For example, suppose that a package specification declares the function factorial this way:
Appropriate Public Items
Appropriate public items are:
Types, variables, constants, subprograms, cursors, and exceptions used by multiple subprograms
A type defined in a package specification is either a PL/SQL user-defined subtype (described in «User-Defined PL/SQL Subtypes») or a PL/SQL composite type (described in Chapter 5, «PL/SQL Collections and Records»).
A PL/SQL composite type defined in a package specification is incompatible with an identically defined local or standalone type (see Example 5-31, Example 5-32, and Example 5-37).
Associative array types of standalone subprogram parameters
You cannot declare an associative array type at schema level. Therefore, to pass an associative array variable as a parameter to a standalone subprogram, you must declare the type of that variable in a package specification. Doing so makes the type available to both the invoked subprogram (which declares a formal parameter of that type) and to the invoking subprogram or anonymous block ( which declares a variable of that type). See Example 10-2.
Variables that must remain available between subprogram invocations in the same session
Subprograms that read and write public variables («get» and «set» subprograms)
Provide these subprograms to discourage package users from reading and writing public variables directly.
Subprograms that invoke each other
You need not worry about compilation order for package subprograms, as you must for standalone subprograms that invoke each other.
Overloaded subprograms are variations of the same subprogram. That is, they have the same name but different formal parameters. For more information about them, see «Overloaded Subprograms».
Creating Package Specifications
To create a package specification, use the «CREATE PACKAGE Statement».
In Example 10-1, the specification for the package trans_data declares two public types and three public variables.
Example 10-1 Simple Package Specification
Example 10-2 Passing Associative Array to Standalone Subprogram
Because the package specifications in Example 10-1 and Example 10-2 do not declare cursors or subprograms, the packages trans_data and aa_pkg do not need bodies.
Package Body
If a package specification declares cursors or subprograms, then a package body is required; otherwise, it is optional. The package body and package specification must be in the same schema.
Every cursor or subprogram declaration in the package specification must have a corresponding definition in the package body. The headings of corresponding subprogram declarations and definitions must match word for word, except for white space.
To create a package body, use the «CREATE PACKAGE BODY Statement».
Example 10-3 Matching Package Specification and Body
Show errors (in SQL*Plus):
The cursors and subprograms declared in the package specification and defined in the package body are public items that can be referenced from outside the package. The package 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.
You can change the package body without changing the specification or the references to the public items.
Package Instantiation and Initialization
When a session references a package item, Oracle Database instantiates the package for that session. Every session that references a package has its own instantiation of that package.
When Oracle Database instantiates a package, it initializes it. Initialization includes whichever of the following are applicable:
Assigning initial values to public constants
Assigning initial values to public variables whose declarations specify them
Executing the initialization part of the package body
Package State
Each session that references a package item has its own instantiation of that package. If the package is stateful, the instantiation includes its state. The package state persists for the life of a session, except in these situations:
The package body is recompiled.
If the body of an instantiated, stateful package is recompiled (either explicitly, with the «ALTER PACKAGE Statement», or implicitly), the next invocation of a subprogram in the package causes Oracle Database to discard the existing package state and raise the exception ORA-04068.
After PL/SQL raises the exception, a reference to the package causes Oracle Database to re-instantiate the package, which re-initializes it. Therefore, previous changes to the package state are lost. (For information about initialization, see «Package Instantiation and Initialization».)
Any of the session’s instantiated packages are invalidated and revalidated.
As of Oracle Database 11 g Release 2 (11.2.0.2), Oracle Database treats a package as stateless if its state is constant for the life of a session (or longer). This is the case for a package whose items are all compile-time constants.
A compile-time constant is a constant whose value the PL/SQL compiler can determine at compilation time. A constant whose initial value is a literal is always a compile-time constant. A constant whose initial value is not a literal, but which the optimizer reduces to a literal, is also a compile-time constant. Whether the PL/SQL optimizer can reduce a nonliteral expression to a literal depends on optimization level. Therefore, a package that is stateless when compiled at one optimization level might be stateful when compiled at a different optimization level. For information about the optimizer, see «PL/SQL Optimizer».
SERIALLY_REUSABLE Packages
SERIALLY_REUSABLE packages let you design applications that manage memory better for scalability.
Trying to access a SERIALLY_REUSABLE package from a database trigger, or from a PL/SQL subprogram invoked by a SQL statement, raises an error.
Creating SERIALLY_REUSABLE Packages
To create a SERIALLY_REUSABLE package, include the SERIALLY_REUSABLE pragma in the package specification and, if it exists, the package body.
Example 10-4 creates two very simple SERIALLY_REUSABLE packages, one with only a specification, and one with both a specification and a body.
Example 10-4 Creating SERIALLY_REUSABLE Packages
SERIALLY_REUSABLE Package Work Unit
For a SERIALLY_REUSABLE package, the work unit is a server call. You must use its public variables only within the work unit.
If you make a mistake and depend on the value of a public variable that was set in a previous work unit, then your program can fail. PL/SQL cannot check for such cases.
Example 10-5 Effect of SERIALLY_REUSABLE Pragma
After the work unit (server call) of a SERIALLY_REUSABLE package completes, Oracle Database does the following:
Closes any open cursors.
Frees some nonreusable memory (for example, memory for collection and long VARCHAR2 variables)
Returns the package instantiation to the pool of reusable instantiations kept for this package.
Explicit Cursors in SERIALLY_REUSABLE Packages
An explicit cursor in a SERIALLY_REUSABLE package remains open until either you close it or its work unit (server call) ends. To re-open the cursor, you must make a new server call. A server call can be different from a subprogram invocation, as Example 10-6 shows.
In contrast, an explicit cursor in a package that is not SERIALLY_REUSABLE remains open until you either close it or disconnect from the session.
Example 10-6 Cursor in SERIALLY_REUSABLE Package Open at Call Boundary
First call to server:
New call to server:
Package Writing Guidelines
Become familiar with the packages that Oracle Database supplies, and avoid writing packages that duplicate their features.
Keep your packages general so that future applications can reuse them.
Design and define the package specifications before the package bodies.
In package specifications, declare only items that must be visible to invoking programs.
This practice prevents other developers from building unsafe dependencies on your implementation details and reduces the need for recompilation.
If you change the package specification, you must recompile any subprograms that invoke the public subprograms of the package. If you change only the package body, you do not have to recompile those subprograms.
Declare public cursors in package specifications and define them in package bodies, as in Example 10-7.
This practice lets you hide cursors’ queries from package users and change them without changing cursor declarations.
Assign initial values in the initialization part of the package body instead of in declarations.
This practice has these advantages:
The code for computing the initial values can be more complex and better documented.
If computing an initial value raises an exception, the initialization part can handle it with its own exception handler.
Example 10-7 Separating Cursor Declaration and Definition in Package
Package Example
The specification declares a public type, cursor, and exception, and three public subprograms. One public subprogram is overloaded (for information about overloaded subprograms, see «Overloaded Subprograms»).
The body declares a private variable, defines the public cursor and subprograms that the specification declares, declares and defines a private function, and has an initialization part.
Example 10-8 Creating emp_admin Package
Result is similar to:
How STANDARD Package Defines the PL/SQL Environment
The contents of package STANDARD are directly visible to applications. You need not qualify references to its contents by prefixing the package name. For example, you might invoke ABS from a database trigger, stored subprogram, Oracle tool, or 3GL application, as follows:
Most SQL functions are overloaded. For example, package STANDARD contains these declarations:
PL/SQL resolves an invocation of TO_CHAR by matching the number and data types of the formal and actual parameters.
20) Пакеты в PL / SQL
Что такое пакет в Oracle?
Пакет PL / SQL представляет собой логическое объединение связанной подпрограммы (процедуры / функции) в один элемент. Пакет компилируется и сохраняется как объект базы данных, который можно использовать позже.
В этом уроке вы узнаете
Компоненты пакетов
Пакет PL / SQL состоит из двух компонентов.
Спецификация упаковки
Спецификация пакета состоит из объявления всех открытых переменных, курсоров, объектов, процедур, функций и исключений.
Ниже приведены некоторые характеристики спецификации пакета.
Синтаксис
Приведенный выше синтаксис показывает создание спецификации пакета.
Корпус
Он состоит из определения всех элементов, которые присутствуют в спецификации пакета. Он также может иметь определение элементов, которые не объявлены в спецификации, эти элементы называются частными элементами и могут вызываться только из пакета.
Ниже приведены характеристики корпуса упаковки.
Синтаксис:
Теперь мы увидим, как ссылаться на элементы пакета в программе.
Ссылающиеся элементы пакета
Как только элементы объявлены и определены в пакете, нам нужно обратиться к элементам, чтобы использовать их.
Создать пакет в PL / SQL
В PL / SQL всякий раз, когда пакет вызывается / вызывается в сеансе, для этого пакета будет создан новый экземпляр.
Oracle предоставляет возможность инициализировать элементы пакета или выполнять какие-либо действия во время создания этого экземпляра с помощью «Инициализация пакета».
Это не что иное, как блок выполнения, который записывается в теле пакета после определения всех элементов пакета. Этот блок будет выполняться всякий раз, когда пакет передается в первый раз в сеансе.
Синтаксис
Форвардные декларации
Прямое объявление / ссылка в пакете — это не что иное, как объявление отдельных элементов по отдельности и определение их в более поздней части тела пакета.
На частные элементы можно ссылаться, только если они уже объявлены в теле пакета. По этой причине используется предварительное объявление. Но это довольно необычно в использовании, потому что в большинстве случаев закрытые элементы объявляются и определяются в первой части тела пакета.
Форвардное объявление — это опция, предоставляемая Oracle, она не обязательна, и использование, а не использование — это требование программиста.
Синтаксис:
Приведенный выше синтаксис показывает предварительное объявление. Закрытые элементы объявляются отдельно в передней части пакета, и они были определены в более поздней части.
Использование курсоров в пакете
В отличие от других элементов, нужно соблюдать осторожность при использовании курсоров внутри пакета.
Если курсор определен в спецификации пакета или в глобальной части тела пакета, то один раз открытый курсор будет сохраняться до конца сеанса.
Поэтому всегда следует использовать атрибуты курсора «% ISOPEN», чтобы проверить состояние курсора, прежде чем ссылаться на него.
перегрузка
Перегрузка — это концепция наличия множества подпрограмм с одинаковым именем. Эти подпрограммы будут отличаться друг от друга количеством параметров или типов параметров или возвращаемого типа, то есть подпрограмма с тем же именем, но с другим количеством параметров, другой тип параметров или другой повторный тип рассматриваются как перегрузка.
Это полезно, когда многие подпрограммы должны выполнять одну и ту же задачу, но способ вызова каждой из них должен быть разным. В этом случае имя подпрограммы будет оставаться одинаковым для всех, а параметры будут изменены в соответствии с оператором вызова.
Пример 1 : В этом примере мы собираемся создать пакет для получения и установки значений информации о сотруднике в таблице «emp». Функция get_record возвращает вывод типа записи для данного номера сотрудника, а процедура set_record вставляет запись типа записи в таблицу emp.
Шаг 1) Создание спецификации пакета
Вывод:
Код Объяснение
Шаг 2) Пакет содержит тело пакета, в котором будут определены все действительные процедуры и функции. На этом шаге создается тело пакета.
Вывод:
Код Объяснение
Шаг 3) Создание анонимного блока для вставки и отображения записей, ссылаясь на созданный выше пакет.
Вывод:
Объяснение кода:
Зависимость в пакетах
Поскольку пакет представляет собой логическую группу связанных вещей, он имеет некоторые зависимости. Ниже приведена зависимость, которую нужно позаботиться.
Информация о пакете
Как только информация о пакете создана, информация о пакете, такая как источник пакета, детали подпрограммы и информация о перегрузке, доступна в таблицах определения данных Oracle.
Ниже в таблице приведены таблица определения данных и информация о пакете, которая доступна в таблице.
Имя таблицы | Описание | запрос |
ALL_OBJECT | Предоставляет сведения о пакете, такие как object_id, creation_date, last_ddl_time и т. Д. Он будет содержать объекты, созданные всеми пользователями. | SELECT * FROM all_objects, где имя_объекта = ‘ ’ |
USER_OBJECT | Предоставляет сведения о пакете, такие как object_id, creation_date, last_ddl_time и т. Д. Он будет содержать объекты, созданные текущим пользователем. | SELECT * FROM user_objects, где имя_объекта = ‘ ’ |
ALL_SOURCE | Предоставляет источник объектов, созданных всеми пользователями. | SELECT * FROM all_source, где name = ‘ ’ |
USER_SOURCE | Предоставляет источник объектов, созданных текущим пользователем. | SELECT * FROM user_source, где name = ‘ ’ |
ALL_PROCEDURES | Предоставляет подробности подпрограммы, такие как object_id, сведения о перегрузке и т. Д., Созданные всеми пользователями. | SELECT * FROM all_procedures где имя_объекта = ‘ ’ |
USER_PROCEDURES | Предоставляет подробности подпрограммы, такие как object_id, сведения о перегрузке и т. Д., Созданные текущим пользователем. | SELECT * FROM user_procedures Где имя_объекта = ‘ ’ |
UTL FILE — обзор
UTL File — это отдельный пакет утилит, предоставляемый Oracle для выполнения специальных задач. Это в основном используется для чтения и записи файлов операционной системы из пакетов PL / SQL или подпрограмм. У него есть отдельные функции для размещения информации и получения информации из файлов. Это также позволяет читать / писать в собственном наборе символов.
Программист может использовать это для записи файлов операционной системы любого типа, и файл будет записан непосредственно на сервер базы данных. Имя и путь к каталогу будут указаны при написании.
Резюме
Теперь мы изучили пакеты на PL / SQL, и теперь вы сможете работать следующим образом.
Когда используются пакеты (package) PL/SQL на примере
Итак, в прошлых моих блогах мы рассмотрели синтаксис, основные правила и нюансы построения пакетов. Теперь можно вернуться к списку ситуаций, в которых пакеты PL/SQL следует применять, и рассмотреть их более подробно.
Ниже каждая из этих причин рассматривается более подробно.
Инкапсуляция доступа к данным
Вместо того чтобы заставлять разработчиков самостоятельно писать команды SQL, предоставьте интерфейс к этим командам. Это одна из самых важных причин для построения пакетов, но она применяется относительно редко.
При таком подходе разработчики PL/SQL вместо команд SQL обычно включают в свои приложения заранее определенный, протестированный и оптимизированный код, который выполняет всю работу за них; например, процедуру add (перегруженную для поддержки записей), которая выдает команду INSERT и следует стандартным правилам обработки ошибок, функцию для выборки одной записи по первичному ключу и разнообразные курсоры для обработки стандартных запросов к структуре данных (которой может быть одна таблица или «бизнес-сущность» из нескольких таблиц).
Если вы выберете этот путь, разработчикам не нужно будет разбираться, как объединить три или шесть нормализованных таблиц для получения нужного набора данных. Они просто выбирают нужный курсор, а анализ данных оставляется кому-то другому. Им не нужно думать, что делать, если при попытке вставки строка уже существует — процедура уже содержит всю необходимую логику.
Вероятно, самое серьезное преимущество такого подхода заключается в том, что при изменении структуры данных проблемы с обновлением кода приложения сводятся к минимуму и централизуются. Человек, хорошо знающий таблицу или объектный тип, вносит необходимые изменения в одном пакете, а эти изменения затем более или менее автоматически распространяются на всех программы, зависящие от этого пакета.
Давайте посмотрим, как использование пакетов отражается на коде. Файл givebonusi. sp на сайте книги содержит процедуру, которая начисляет одинаковые премии всем работникам заданного отдела, — но только при условии, что стаж работника в компании составляет не менее 6 месяцев. Ниже приведены части программы give_bonus с кодом SQL (полная реализация содержится в файле givebonusi.sp ):
Сравните с альтернативным решением, полный код которого содержится в файле givebonus2.sp :
В следующей таблице объясняются изменения, внесенные во второй версии.
В целом команды SQL были исключены из программы и заменены вызовами процедур и функций, предназначенных для многократного использования. В моем приложении такое решение оптимизирует SQL и способствует более эффективному написанию более надежного кода. Построение (или генерирование) таких пакетов ни в коей мере не является тривиальным делом, я и понимаю, что большинство читателей вряд ли захотят или смогут использовать «чистый» инкапсулированный подход. Однако многие преимущества инкапсуляции данных могут использоваться и без полной переработки стиля программирования. Как минимум я рекомендую:
Исключение жесткого кодирования литералов
Практически в каждом приложении используются всевозможные «волшебные значения» — литералы, имеющие особый смысл для системы. Например, это могут быть коды типов или граничные значения для проверки данных. Конечно, пользователи скажут вам, что волшебные значения никогда не изменяются. «В моем балансе всегда будет ровно 25 позиций», — говорит один. «Родительская компания всегда будет называться ATLAS HQ», — клянется другой. Не верьте клятвам и никогда не фиксируйте их в своих программах. Возьмем следующие команды IF:
Тот, кто пишет подобный код, сам напрашивается на неприятности. Ваша жизнь намного упростится, если вы создадите пакет с именованными константами:
С таким пакетом две предшествующие команды IF принимают следующий вид:
Если в будущем какое-либо из «волшебных значений» изменится, достаточно изменить соответствующую константу в конфигурационном пакете. Вносить изменения в остальных модулях не нужно. Практически в каждом приложении из тех, которые я рецензировал (и некоторые из тех, что я написал), ошибочно включались жестко закодированные «волшебные значения». В каждом случае разработчику приходилось вносить множественные изменения в программы — как в фазе разработки, так и в фазе сопровождения. Иногда это создает проблемы, иногда оборачивается сущим кошмаром; я не могу выразить словами, насколько важно консолидировать все «волшебные значения» в одном или нескольких пакетах.
Наконец, если вам доведется выбирать литеральные значения, которые планируется скрыть за константами, старайтесь использовать невероятные значения, которые вряд ли будут использоваться как литералы. Предположим, процедура должна возвращать индикатор состояния: успех или неудача? Типичные значения таких флагов — 0 и 1, S и F и т. д. Тем не менее у таких значений есть один недостаток: они коротки и интуитивно понятны, поэтому у недисциплинированного программиста возникает соблазн «смухлевать» и напрямую использовать литералы в коде. Возьмем следующий пример:
Скорее всего, с таким определением вы столкнетесь с использованием big_stuff следующего вида:
С другой стороны, если спецификация пакета выглядит так:
вы никогда не увидите такой код:
Он выглядит просто неприлично.
Устранение недостатков встроенных функций
Вместо того чтобы заполнять страницы книги примерами, я перечислю файлы нескольких пакетов, размещенных на сайте книги. Эти примеры демонстрируют использование пакетов, а также ряд других полезных возможностей. Я рекомендую просмотреть файлы *.pkg на сайте — вы найдете в них код, который может пригодиться в ваших приложениях. С чего стоит начать?
Группировка логически связанных функций
Если ваша программа содержит десяток процедур и функций, связанных с конкретной функциональностью или аспектом вашего приложения, разместите их в пакете, чтобы упростить управление этим кодом (и его поиск). Это особенно важно при программировании бизнес-правил приложения, в реализации которых следует соблюдать некоторые важные правила:
Прежде чем браться за построение приложения, сконструируйте набор пакетов, инкапсулирующих все его правила. Иногда эти правила являются частью большего пакета — например, пакета инкапсуляции таблиц. В других случаях можно создать пакет, который не содержит ничего, кроме ключевых правил, как в следующем примере:
Конечно, не вся «логически связанная функциональность» имеет отношение к бизнесправилам. Допустим, мне нужно расширить возможности встроенных функций PL/ SQL для работы со строками. Вместо того чтобы создавать 12 разных функций, я создаю пакет, размещаю все функции в этом пакете и сообщаю другим разработчикам, как обратиться к этой функциональности.
Кэширование статических данных сеанса для ускорения работы приложения
Используйте данные пакетов для улучшения времени отклика приложения посредством кэширования статических данных (без повторных запросов). Это можно сделать на нескольких уровнях. Для каждого варианта в следующем списке я привожу несколько полезных примеров кода, доступных на сайте книги.
Кэширование на базе пакетов — всего лишь одна из разновидностей кэширования, доступных для разработчиков PL/SQL.
Если вы решите использовать кэширование уровня пакета, помните, что данные кэшируются по отдельности для каждого сеанса, обращающегося к пакету (в глобальной области PGA базы данных Oracle). Таким образом, если кэш занимает 2 Мбайт и в системе существует 1000 одновременно подключенных сеансов, вы расходуете 2 Гбайт памяти своей системы — кроме всей остальной памяти, расходуемой базой данных.