Пример объектного приложения PL/SQL
Идея для примера этого блога взята из книги Learning Oracle PL/SQL
(изд-во O’Reilly). Мы построим систему на базе Oracle
, в которой объектно-ориентированный подход используется для реализации библиотечного каталога. В каталоге хранится информация о книгах, периодических изданиях (журналах и газетах) и других печатных изданиях.
Рис. 1. Иерархия типов библиотечного каталога
Графическое представление типов верхнего уровня каталога можно увидеть на рис. 1. В дальнейшем иерархия будет дополнена объектами, обозначенными пунктиром.
Создание базового типа
Корневой элемент (вершина иерархии) представляет общие характеристики всех подтипов. Допустим, что у книг и периодики имеются только две общие характеристики: библиотечный идентификатор и название. Таким образом, объектный тип для элемента каталога создается в SQL*Plus
следующей командой SQL
:
CREATE OR REPLACE TYPE catalog_item_t AS OBJECT (
id INTEGER,
title VARCHAR2(4000),
NOT INSTANTIABLE MEMBER FUNCTION ck_digit_okay
RETURN BOOLEAN,
MEMBER FUNCTION print
RETURN VARCHAR2
) NOT INSTANTIABLE NOT FINAL;
Команда создает объектный тип — аналог класса Java
или C++
. Если провести аналогию с реляционной моделью, объектный тип подобен типу записи с набором связанных с ним функций и процедур (называемых методами).
Ключевые слова NOT FINAL
в конце определения типа указывают, что он может служить базовым или супертипом, то есть на его основе могут определяться другие типы. В данном случае мы собираемся создать подтипы для книг и периодических изданий; если опустить эти ключевые слова, по умолчанию Oracle
использует слово FINAL
, запрещающее создание подтипов на основе данного типа.
Обратите внимание на то, что спецификация данного типа содержит предложение NOT INSTANTIABLE
. Это означает, что PL/SQL
позволит объявлять переменные типа catalog_item_t
, но им нельзя будет присваивать значения — ни явно, ни другим способом. Подобно абстрактному классу Java
, данный тип предназначен исключительно для использования в качестве основы для создания подтипов (для которых, конечно, можно будет создавать и экземпляры объектов).
Для удобства в тип включен метод print
(кстати, это не зарезервированное слово), который позволяет получить описание объекта в виде одной строки. При создании подтипа метод будет перегружен, то есть подтип будет содержать метод с тем же именем, но возвращающий атрибуты подтипа. Обратите внимание: вместо оформления print
в виде процедуры, из-за чего в программе было бы зафиксировано использование DBMS_OUTPUT. PUT_LINE
или чего-нибудь в этом роде, я решил использовать функцию, вывод которой можно будет позднее перенаправить. Такое решение не является объектно-ориентированным; просто признак хорошего проектирования.
Также определяется метод ck_digit_okay
, который возвращает TRUE
или FALSE
в зависимости от того, совпала ли контрольная цифра. Предполагается, что все объекты подтипов catalog_item_t
будут содержать идентификаторы, включающие контрольную цифру для проверки правильности записи. Поскольку мы собираемся работать только с книгами и периодикой, которые обычно идентифицируются кодами ISBN
и ISSN
, то к этим подтипам применима концепция контрольной цифры.
Прежде чем переходить к следующей части примера, обратите внимание на некоторые обстоятельства:
- Приведенная выше команда
CREATE
TYPE
создает спецификацию объектного типа. Соответствующее тело типа с реализацией методов создается отдельно командойCREATE
TYPE BODY
. - Для объектных типов используется то же пространство имен, что для таблиц и программ
PL/SQL
верхнего уровня. Это одна из причин, по которым в именах типов используется префикс «_t». - Объектные типы всегда принадлежат создавшему их пользователю (схеме)
Oracle
,который может предоставлять привилегиюEXECUTE
для них другим пользователям. - Синонимы объектных типов поддерживаются только начиная с
Oracle9i Release 2
и выше. - Как и традиционные программы
PL/SQL
, объектные типы можно определять с разрешениями создателя (по умолчанию) или вызывающего (см. эту статью). - В отличие от объектных моделей некоторых языков, в объектной модели
Oracle
отсутствует корневой класс, от которого образуются все остальные пользовательские классы. - Если вы столкнетесь с ошибкой компиляции
PLS
-00103, вероятно, вы допустили распространенную ошибку и завершили метод символом «;». В этой спецификации типа должна использоваться запятая.
Создание подтипа
Поскольку для типа catalog_item_t
не могут создаваться экземпляры, нужно определить его подтипы. Начнем с подтипа для объектов-книг. Так как книга является одним из элементов каталога, в нашем примере все экземпляры подтипа book_t
будут обладать четырьмя атрибутами:
-
id
— наследуется от базового типаcatalog_item_t
; -
title
— также наследуется от базового типа; -
isbn
— соответствует кодуISBN
книги; -
pages
— количество страниц книги.
Соответствующий программный код выглядит так:
1 TYPE book_t UNDER catalog_item_t (
2 isbn VARCHAR2(13),
3 pages INTEGER,
4
5 CONSTRUCTOR FUNCTION book_t (id IN INTEGER DEFAULT NULL,
6 title IN VARCHAR2 DEFAULT NULL,
7 isbn IN VARCHAR2 DEFAULT NULL,
8 pages IN INTEGER DEFAULT NULL)
9 RETURN SELF AS RESULT,
10
11 OVERRIDING MEMBER FUNCTION ck_digit_okay
12 RETURN BOOLEAN,
13
14 OVERRIDING MEMBER FUNCTION print
15 RETURN VARCHAR2
16 );
Интересные части кода описаны в следующей таблице.
Строки | Описание |
1 | Для определения подтипа используется ключевое слово UNDER . Конструкция AS OBJECT здесь не используется, потому что она будет лишней: производным от объектного типа может быть только другой объектный тип |
2-3 | Перечисляются только те атрибуты, которые уникальны для подтипа; атрибуты родительского типа включаются в подтип автоматически. Oracle сначала создает атрибуты базового типа, а затем атрибуты подтипа в порядке их следования в спецификации |
5-15 | Объявления методов (см. следующий раздел) |