Введение в объектную технологию в PL/SQL8
Введение в объектную технологию в PL/SQL8.
16 марта 2000 г.
Making sense of object technology in PL/SQL8
Билл Прибил, DataCraft Inc и Стивен Фейрстайн, PL/Solutions&RevealNet
Введение.
Объекты, методы, наборы, ссылки (REFs), распределение ролей, объектные представления. Тпру! Что это за новшества в PL/SQL? Зачем все это нужно? Как сравнить эти новые особенности со старыми? Вот некоторые вопросы, которые затрагиваются в этой статье. На некоторые из них есть даже ответы!
Объектная опция (Object Option) Oracle, доступная, начиная с Oracle8, включает три основных новых вида конструкций, которые программисты PL/SQL должны знать:
Объектные типы - это средство для создания сложных пользовательских типов данных и объявления объектных "методов", которые могут оперировать этими типами. "Сложные" они потому, что могут состоять более чем из одного атрибута и реализуют концепцию абстрактных типов данных.
Вложенные таблицы (nested table) и массивы переменной длины (VARRAY) - новые типы данных набора, похожие на PL/SQL таблицы в Oracle7, которые могут использоваться теперь для хранения сложных данных в элементах таблицы.
Объектные представления - способ присоединения типов объектов и наборов к реляционной системе.
Хотя все три категории конструкций могут с успехом использоваться в новых приложениях, третья категория, объектные представления, занимает особое место в среде Oracle при переносе инструментария и методов в объектную технологию. Рассмотрим теперь каждую из конструкций, и то как она сочетается с другими конструкциями Oracle.
Почему удобно использование объектов?
Объектная технология реализует идею, что и модель, и реализация, будут происходить от "предметов" чаще, чем от "процессов". Такой взгляд находит мало сторонников среди профессионалов баз данных, для которых данные долгое время были центром вселенной. Объектный подход придает особое значение объединению поведения предмета с его данными. Основной идеей объектно-ориентированного анализа, дизайна и программирования является то, что эта технология помогает создавать приложения, наиболее естественно обращающиеся к предметной области, и обладающие достоинствами, позволяющими повторно использовать код.
Вопрос, почему удобно использование объектов, может разбудить страсти, подобные религиозным. Основные доводы за использование объектов, возможно, несколько спорные, приведены ниже:
Правильно использованная объектная технология может повысить производительность и надежность, при одновременном уменьшении сложности и стоимости жизненного цикла приложений.
С ростом популярности C++ и, частично, Java, объектная технология становится основным направлением компьютерного программирования, и традиционные базы данных обречены на новую модель.
В некоторых приложениях необходимо использовать сложные структуры данных, которые не укладываются, интуитивно или эффективно, в строки и столбцы, а объекты предоставляют нескалярные структуры, которые могут содержать и оперировать такими данными.
С тех пор как объектные и реляционные модели позволяют "полное сопротивление несоответствию" ("impedance mismatch"), использование объектно-ориентированного программирования вместе с реляционной базой данных, в лучшем случае, является исторически сложившейся проблемой.
Объектные типы.
Первым большим затруднением, которое необходимо преодолеть, является терминология. Например, даже до восьмой версии Oracle использовал объекты - такие, как таблицы, индексы, пакеты, процедуры и так далее - и действительно, их можно увидеть в представлении USER_OBJECTS. В новой версии имеется нечто, называющееся более точно объектные типы, которые могут иметь экземпляры объектов, последние могут ссылаться на обычные объекты. Сбивает с толку, не так ли?
Объектные типы - это конструкция базы данных Oracle, управляемая через расширение DDL, которая объявляет структуру данных (атрибуты) и разрешенные операции (методы) над атрибутами. Тип - это только шаблон, он не содержит никаких данных; можно создавать переменные, таблицы, столбцы и другие конструкции этого типа. Если вы знакомы с объектной терминологией, отметьте, что объектный тип сродни классу. Он также очень похож на абстрактный тип данных.
Объект - это экземпляр объектного типа Oracle8. Объект - это место, где живут актуальные данные. Объекты могут храниться в таблицах, и, в таких случаях, они являются постоянными, либо они могут существовать только временно в переменных PL/SQL.
Атрибут - это структурная часть объекта Oracle, приблизительно похожая на столбец таблицы. Каждый атрибут может принадлежать одному типу данных, либо скалярному, как VARCHAR2 или INTEGER, либо составному, как объявленные пользователем вложенные таблицы или другие (вложенные) объекты. Скалярные атрибуты иногда называют простыми, а составные атрибуты могут упоминаться как сложные.
Метод - это процедура или функция, обычно выполняемая в PL/SQL, которая (как правило) производит операции над атрибутами объекта. Методы для объекта могут быть вызваны только в контексте конкретного объекта этого типа. Методы могут быть созданы и на языке C, тогда они вызываются как "внешние процедуры" Oracle. Существует специальный метод по умолчанию (default), поддерживаемый Oracle, называемый конструктор (constructor), который инициализирует объекты.
Некоторые простые примеры.
Давайте рассмотрим несколько простых примеров, иллюстрирующих использование объектов. Прежде всего, нужно объявить объектный тип.
CREATE TYPE Pet_t AS OBJECT (
tag_no INTEGER,
name VARCHAR2(60),
MEMBER FUNCTION set_tag_no (new_tag_no IN INTEGER) RETURN Pet_t);
Этот объектный тип имеет два атрибута, tag_no и name, и один метод, set_tag_no. Необходимо также создать "тело объектного типа " для поддержки тела метода.
CREATE TYPE BODY Pet_t AS
MEMBER FUNCTION set_tag_no (new_tag_no IN INTEGER) RETURN Pet_t IS
the_pet Pet_t := SELF; -- initialize to "current" object
BEGIN
the_pet.tag_no := new_tag_no;
RETURN the_pet;
END;
END;
Этот объектный тип используется далее в нескольких фрагментах кода, иллюстрирующих различное применение типов.
Строчные объекты.
Прежде всего, каждая строка в таблице может принадлежать объектному типу данных. В этом случае таблица описывается как объектная таблица, содержащая строчные объекты (row objects), что означает, что каждая строка является экземпляром объекта.
CREATE TABLE pets OF Pet_t;
Объектная таблица, такая как pets, имеет специальный скрытый столбец, который называется идентификатор объекта (object identifier) или OID. Этот идентификатор является уникальным не только среди всех таблиц, но и среди всей базы данных! Поскольку OID доступен для программиста, в других таблицах можно хранить ссылки, указывающие на строку-объект. Такие указатели называются ссылки (REF), они работают подобно внешним ключам.
После создания объектной таблицы pets, можно создать объект (экземпляр объекта) типа Pet_t, используя "конструктор по умолчанию" (default constructor) - специальный метод, автоматически поддерживаемый Oracle при создании пользовательского типа данных. Этот конструктор имеет то же имя, что и объектный тип, и имеет по одному аргументу на каждый атрибут, объявленный в типе.
INSERT INTO pets VALUES (Pet_t(23052, 'Mambo'));
Поскольку Oracle8 является "объектно-реляционной" СУБД, можно использовать и знакомый синтаксис для выполнения аналогичной вставки.
INSERT INTO pets VALUES (23052, 'Mambo');
Столбцовые объекты.
Объектный тип может также быть типом данных столбца в таблице. В этом случае говорят, что столбец содержит столбцовые объекты (column objects). Различные столбцы в таблице могут быть разных объектных типов. (Пример, приведенный ниже, использует объект Address_t, допустим, уже объявленный. Он содержит атрибуты и методы, предназначенные для адресов.)
CREATE TABLE families (
surname VARCHAR2(50),
favorite_pet Pet_t,
address Address_t);
Временные объекты.
Объект может иметь тип данных "локальная переменная" (local variable). В этом случае, объявление и инициализация объектной переменной выполняется одним оператором. При инициализации используется автоматически доступный конструктор, который имеет то же имя, что и тип данных.
DECLARE
my_pet Pet_t := Pet_t(23052, 'Mambo');
Объект может иметь тип данных "формальный параметер PL/SQL" (PL/SQL formal parameter). Как показано в примере, функции также могут возвращать объектные типы. Оператор VALUE необходим для возвращения объектной таблицы.
CREATE FUNCTION find_pet (the_tag_no IN NUMBER) RETURN Pet_t IS
the_pet Pet_t;
CURSOR pet_cur IS SELECT VALUE(p) FROM pets P
WHERE tag_no = the_tag_no;
BEGIN
OPEN pet_cur;
FETCH pet_cur INTO the_pet;
CLOSE pet_cur;
RETURN the_pet;
END;
Сравнение: Характерные черты объектов Oracle8 и предыдущих версий.
Гораздо чаще объектные типы Oracle8 хранятся в таблицах базы данных Oracle, хотя они используются и внутри программ PL/SQL. Поэтому вне PL/SQL необходимо использовать команду CREATE TYPE … AS OBJECT, даже если этот тип будет использоваться внутри PL/SQL.
Объектный тип немного похож на пакет, который содержит только описание типов и функции, работающие с этими типами. Сходство усиливает то, что объектный тип, как и пакет, может иметь отдельную секцию "тело", в которой описываются процедуры и функции (методы). Однако, существуют и принципиальные отличия, возможно, более значимые, код тела типа объекта может быть вызван только для определенных объектов. Поэтому нельзя вызвать метод, если не определен экземпляр объекта, к которому он должен быть применен. Кроме того, нельзя создать таблицу, основанную на описании пакета, в то время как создать таблицу из объектного типа таким способом можно; объектные типы не могут содержать константы, исключения, курсоры или типы данных. В таблице 1 приведено сравнение основных характеристик новых объектов с основными чертами таблиц и пакетов.
Таблица 1
Характе-ристика |
Таблица Oracle7 |
Пакет Oracle7 |
Объектный тип Oracle8 |
Экземпляр объекта Oracle8 |
Хранит данные |
Да |
Только временно; пакетные переменные существуют в течении сессии |
Нет |
Да, либо постоянно (в таблицах), либо временно (в переменных PL/SQL) |
Служит в качестве шаблона |
Нет |
Нет |
Да |
Нет |
Может содержать сложные данные |
Да (необходима инсталяция Objects Option) |
Да, некоторые типы данных, такие как RECORD или TABLE, не требуют Objects Option |
Не определено (поскольку объектные типы не содержат данных) |
Да (необходима инсталяция Objects Option) |
Содержит процедурный код |
Нет (кроме триггеров таблиц) |
Да |
Да |
Код находится в определении объектного типа, и может быть вызван только для конкретного экземпляра |
Тело существует отдельно от специфи-кации |
N/A (в случае триггеров, ответ - нет) |
Да |
Да |
N/A (в определении типа, но не в экземпляре) |
Может исполь-зовать константы, исключения, курсоры или типы данных |
N/A(в случае триггеров, ответ - нет) |
Да |
Нет |
N/A |
Права модели |
Пользователь должен явно дать DML привилегии на таблицу пользователю или роли |
Если владелец дает привилегию EXECUTE пользователю, то последний наследует от владельца привилегии DML |
Владелец должен дать пользователю привилегию EXECUTE, для того чтобы использовать тип |
В версии 8.0 пользователь присваивает права владельца, в более поздних версиях будут присутствовать права пользователя |
Стратегия использования пакетных и объектных типов.
Существует по крайней мере пять различных способов комбинирования пакетных и объектных типов.
Разрешить полное использование традиционных команд SELECT, INSERT, UPDATE и DELETE для постоянных объектов. Объектная опция будет, в этом случае, выглядеть почти как обычный реляционный подход. В этом случае, не нужно даже объявлять никаких методов, но за это придется расплачиваться.
Указание: По возможности избегайте этого способа, по тем же причинам обычно стараются избегать чрезмерного использования глобальных переменных в приложениях.
Разрешить ограниченное использование традиционных SQL операторов, но вызывать метод "конструктор" в INSERT, и создать различные UPDATE методы, которые будут вызываться в предложениях команды UPDATE. Использовать DELETE, как в предыдущем способе. Этот способ лучше предыдущего, так как при этом можно основываться, по крайней мере частично, на стержневой логике, заложенной в методах. Однако, корректный вызов методов, по-прежнему, описывается при программировании приложения.
Указание: По возможности избегайте этого способа, поскольку это половинчатый подход.
Выполнять все манипуляции над данными через методы, включая все DML-операции над постоянными объектными таблицами (или по крайней мере попытаться сделать это). Если вы пришли из сферы объектного программирования, то это должно быть основным подходом. При таком подходе приложения принимают объектный уклон. Кроме того, это связывает объектный тип с частичным выполнением, что может ограничить повторное использование.
Указание: Сделайте попытку, но остерегайтесь результатов изменения схемы!
Проектируйте объектные методы так, чтобы избежать ссылок на постоянные объектные таблицы, вместо этого работайте только с SELF объектом и с данными, которые передаются через аргументы метода. Создайте пакеты PL/SQL для управления постоянными объектными таблицами (Это похоже на то, что можно было делать в Oracle7), но напишите эти пакеты так, чтобы повторно использовать логику, которая находится в описании объектного типа. Когда PL/SQL приложениям необходимо работать с постоянными данными, они должны вызывать пакет, если же нужно просто выполнить операцию над объектной переменной, то обычно вызывается метод. Подход 4 имеет те же преимущества, что и описанный выше подход 2, но в этом случае значительно увеличивается вероятность того, что программисты будут корректно использовать вызовы в своих приложениях.
Указание: Хорошая (но не оптимальная) стратегия. Впрочем, оптимальных стратегий не существует.
Пятый подход основывается на том, что Oracle поддерживает наследование. Возможно, имеет смысл реализовать постоянные объектные типы как подтипы соответствующего промежуточного объектного типа. При этом можно обеспечить потенциальный успех инкапсуляции и повторного использования при одновременном решении проблем изменения сложной схемы. Однако, подтипы должны учитывать возможность специализированного поведения своих супертипов так, чтобы при каждом незначительном изменении описания объекта не приходилось полностью перестраивать дерево зависимостей.
Указание: Жди и смотри.
Вложенные таблицы и массивы переменного размера.
В PL/SQL версии 2 Oracle представил тип данных TABLE (таблица) как способ хранения одномерных разреженных массивов в PL/SQL. Эта структура известна как "таблица PL/SQL", она полностью описана во многих источниках, в частности в [Feuer97]. В Oracle8 объектная опция (Object Option) представляет две новых структуры для набора, которые имеют широкую сферу применения. Эти структуры называются "вложенные таблицы" (nested table) и "массивы переменного размера" (VARRAY). Как и таблицы PL/SQL, новые структуры могут использоваться в программах PL/SQL. Но действительно новой является возможность использования новых наборов в качестве типов данных полей постоянных таблиц и атрибутов объектов. Пока не существует полноценной реализации типов данных пользователя, наборы предлагают богатые физические (а также логические) дизайнерские возможности для программистов Oracle.
Ниже приводится краткое описание трех типов наборов, доступных в Oracle8:
Таблицы PL/SQL являются одномерным, неограниченным, разреженным набором однородных элементов, доступны только в PL/SQL. Теперь они называются индекс-подобные таблицы (index-by table).
Вложенные таблицы - также одномерные, неограниченные наборы однородных элементов. Изначально они являются компактными, но могут стать разреженными вследствие удалений. Вложенные таблицы могут использоваться как в PL/SQL, так и в базе данных (например, как столбец таблицы).
Массивы переменного размера (VARRAY), как и два других типа, являются одномерными наборами однородных элементов. Однако, они всегда ограниченного размера и никогда не бывают разреженными. Как и вложенные таблицы, они могут быть использованы и в PL/SQL, и в базе данных. В отличии от вложенных таблиц, при хранении и выборке из массивов переменного размера порядок элементов сохраняется.
Используя вложенные таблицы и массивы переменного размера, можно хранить и выбирать сложные данные в один столбец. Например, таблица сотрудников, используемая отделом HP, может хранить данные о дне рождения для каждого сотрудника в отдельном столбце. Создать такую таблицу совсем не сложно. Во-первых, нужно объявить тип набора :
CREATE TYPE Dependent_birthdate_t AS VARRAY(10) OF DATE;
Теперь можно использовать его при описании таблицы:
CREATE TABLE employees (
id NUMBER,
name VARCHAR2(50),
dependents_ages Dependent_birthdate_t
);
Можно заполнить эту таблицу, используя следующее предложение INSERT, которое относится к типу конструкторов по умолчанию, для преобразования списка дат в значение правильного типа данных:
INSERT INTO employees VALUES (42, 'Zaphod Beeblebrox',
Dependent_birthdate_t( '12-JAN-1765', '4-JUL-1977', '22-MAR-2021'));
Весь набор был выбран из базы данных за один прием (fetch). Только одно это свойство может иметь значительное положительное влияние на выполнение приложения.
Таблица 2.
Характе-ристика |
Индекс-подобная таблица |
Вложенная таблица |
Массив |
Размерность |
Одномерный |
Одномерный |
Одномерный |
Использование SQL |
Нет |
Да |
Да |
Использование в качестве типа столбца в БД |
Нет |
Да, данные хранятся "снаружи" (в отдельной таблице) |
Да, данные хранятся "внутри" (в той же таблице) |
Неинциализи-рованное состояние |
Пустая (не может быть null ); элементы не определены |
Элементы - null ; запрещено для элементов-ссылок |
Элементы - null ; запрещено для элементов-ссылок |
Инициализация |
Автоматически, при объявлении |
Через конструктор, выборку(fetch ), присвоение |
Через конструктор, выборку(fetch ), присвоение |
В PL/SQL, элементы через которые ссылается |
BINARY_INTEGER(-2,147,483,647 …2,147,483,647) |
Положительное целое между 1 и 2,147,483,647 |
Положительное целое между 1 и 2,147,483,647 |
Разреженный? |
Да |
Изначально-нет, после удалений - да |
Нет |
Ограниченный? |
Нет |
Может быть расширена |
Да |
Ограничены ли значения индекса (номера элемента массива)? |
Нет, можно использовать любое значение |
Да, Oracle присваивает индексы |
Как во вложенных таблицах, но выход за пределы массива может вызвать исключение |
Средства обеспечения расширения |
Присваивает значение элементу с новым индексом |
Использует встроенную процедуру EXTEND(или TRIM для сжатия), отсутствует заранее определенный максимум |
EXTEND (или TRIM ), но только до размера, заданного при объявлении |
Может сравниваться на равенство |
Нет |
Нет |
Нет |
Сохраняет порядок и номера элементов при сохранении в БД |
Не определено |
Нет |
Да |
Сравнение типов наборов в Oracle8.
Внутри PL/SQL, и вложенные таблицы, и массивы переменного размера являются упорядоченными наборами однородных элементов. Они оба имеют некоторое сходство с типом данных таблицы PL/SQL, старейшим членом семейства "наборов". Новые типы также являются одномерными массивами, но отличаются в деталях таких как разреженность (не обязательно), способ инциализации (через конструктор) и могут ли они принимать значение null (да).
Одно важное отличие между вложенными таблицами и массивами обнаруживается при использовании их в качестве типов данных столбца. Хотя при использовании в качестве типа данных столбца массива можно достичь почти тех же самых результатов, что и при использовании вложенных таблиц; для данных типа VARRAY при объявлении должен быть указан максимальный размер, и фактически они хранятся "внутри" вместе с остальными данными таблицы.
Данные во вложенных таблицах, напротив, хранятся в специальных дополнительных таблицах, называемых таблицами хранения (store tables). Эти данные не ограниченны заранее заданным размером. По этой причине Oracle предлагает использовать столбцы типа VARRAY для "маленьких" массивов, а типа NESTED TABLE - для "больших" массивов.
Старый тип данных, таблицы версии 2.0, являющийся особым случаем нового типа - вложенные таблицы, теперь называется индекс-подобные таблицы, в связи с их уникальным требованием быть индексированными бинарными целыми. Несмотря на большой успех новых наборов данных, индекс-подобные таблицы имеют одну важную особенность: изначальную разреженность. Таблица 2 иллюстрирует множество дополнительных различий между индекс-подобными таблицами и новыми типами наборов.
Стратегии для наборов.
Существует две основные стратегии: первая, определение какой из трех типов наборов соответствует данной проблеме; и вторая, извлекающая какую-либо иную выгоду из набора в вашем приложении.
Какой тип набора использовать?
Если необходимо хранить большое количество постоянных данных в наборе столбца, нужно использовать только вложенные таблицы. В этом случае Oracle будет неявно использовать отдельные таблицы для хранения набора данных, поэтому возможен почти неограниченный рост количества данных.
Если нужно сохранить порядок элементов, хранящихся в наборе столбца, и количество данных невелико, то используйте массив. Что значит "невелико"? Я склоняюсь к мысли, что данных должно быть столько сколько может разместиться в одном блоке базы данных, если данные будут размещены в нескольких блоках, то получится цепочка строк, что замедлит выполнение. Размер блока устанавливается при создании базы данных и обычно равен 2К, 4К или 8К.
Другие показания для выбора типа VARRAY: нежелание беспокоиться об удалениях, поисходящих в середине множества данных; наличие у данных верхней границы; либо необходимость возвращать полный набор одновременно.
Если необходимо использовать разреженные PL/SQL таблицы, скажем, для хранения "интеллектуальных данных", то единственный практический выбор это индекс-подобная таблица. Действительно,вы можете размещать и затем удалять элементы переменной типа NESTED TABLE, но это имеет смысл использовать только для маленьких наборов данных.
Если PL/SQL программа должна работать как в среде Oracle7, так и в среде Oracle8, то вновь единственный вариант - это индекс-подобный набор. Или, если в PL/SQL приложении необходимо использовать отрицательные индексы (номера элементов массива), то опять-таки нужно использовать индекс-подобные таблицы.
Удобство выборки и сохранения данных.
Одной из замечательных особенностей наборов, которая еще не была рассмотрена, является возможность PL/SQL выбирать и сохранять их за одно обращение к базе данных. Без наборов, даже при выборке из соединения главной и подчиненной таблиц, всегда необходимо несколько обращений для выборки нескольких строк. При выборке и сохранении типа набора можно не только уменьшить трафик сети, но и сделать свою программу более краткой.
Используя операторы CAST и MULTISET можно использовать эти преимущества даже в реляционных таблицах.
Объектные представления.
Хотя объектные расширения Oracle предлагают широкие возможности для проектирования новых систем, некоторые работающие с Oracle организации, использующие большие реляционные базы данных, захотят или будут вынуждены полностью перестроить свои системы, чтобы использовать объекты. Для того, чтобы позволить уже созданным приложениям использовать преимущества новых средств, Oracle8 предоставляет объектные представления. Использование объектных представлений дает следующие возможности:
Эффективный доступ к объекту. В PL/SQL, и частично в приложениях Oracle Call Interface (OCI), объектное программирование предоставляет средства для удобной выборки, кеширования и обновления объектных данных. Эти программные возможности могут обеспечить улучшение выполнения, при этом приложение может стать более кратким.
Возможность навигации с использованием ссылок. При использовании уникальных идентификаторов в качестве основы для идентификаторов объекта (OID), можно применять объектную навигацию. Например, можно выбрать атрибуты из реляционных "виртуальных объектов", используя точечную нотацию вместо явных соединений (joins).
Упрощение изменения схемы. В ранних версиях Oracle8, в результате использования чисто объектного подхода почти любое изменение схемы оборачивалось кошмаром. Напротив, объектные представления предлагают различные способы изменения как структуры таблицы, так и описания типа объекта существующей системы.
Совместимость с новыми объектно-ориентированными приложениями. При необходимости изменения дизайна ранее созданной базы данных, новые компоненты могут быть реализованы в объектных таблицах; новые объектно-ориентированные приложения для доступа к существующим данным могут использовать совместимую программную модель. Ранее созданные приложения могут продолжать работать без изменений.
Другие новые возможности Oracle могут улучшить выразительность любого типа представления, не только объектного. Существует две возможности, определенные не только для объектных представлений, это наборы и триггеры "INSTEAD OF". Рассмотрим две реляционные таблицы, связанные простым отношением мастер-деталь. Используя объектную опцию Oracle, можно описать подчиненные записи как одномерный нескалярный атрибут (набор) основной записи, это может оказаться очень удобной абстракцией. Кроме того, используя триггеры "INSTEAD OF", можно точно сказать Oracle как выполнять вставки, обновления и удаления в представлениях. Эти две возможности доступны как для объектных представлений, так и для необъектных.
Примеры объектных представлений.
В этом примере показано как объектные представления могут быть использованы в воображаемой фирме, проектирующей web узлы. Существует реляционное приложение, отслеживающее JPEG, GIF, и другие изображения, которые используются при проектировании клиентских web-узлов. Сами изображения хранятся в файлах, а данные о них находятся в реляционных таблицах. Чтобы помочь мастерам графики определить местонахождение нужных изображений, каждое изображение имеет одно или несколько связанных с ним ключевых слов, которые хранятся в простом отношении мастер-деталь.
Существующая система имеет одну таблицу для метаданных об изображениях:
CREATE TABLE images (
image_id INTEGER NOT NULL,
file_name VARCHAR2(512),
file_type VARCHAR2(12),
bytes INTEGER,
CONSTRAINT image_pk PRIMARY KEY (image_id));
...и одну таблицу ключевых слов, связанную с изображениями:
CREATE TABLE keywords (
image_id INTEGER NOT NULL,
keyword VARCHAR2(45) NOT NULL,
CONSTRAINT keywords_pk PRIMARY KEY (image_id, keyword),
CONSTRAINT keywords_for_image FOREIGN KEY (image_id)
REFERENCES images (image_id));
Для создания наиболее полезной абстракции, имеет смысл логически слить эти две таблицы в одно объектное представление. Чтобы сделать это, необходимо, во-первых, создать объектный тип с соответствующими атрибутами. Поскольку обычно для заданного изображения существует небольшое количество ключевых слов, это отношение пригодно для использования набора для хранения ключевых слов.
Перед тем как создать верхний уровень, необходимо, прежде всего, объявить набор для хранения ключевых слов.
CREATE TYPE Keyword_tab_t AS TABLE OF VARCHAR2(45);
Легко видеть, что объявить объектный тип совсем несложно. Чтобы сделать пример короче, объявлено только пара методов. В нижеследующем описании типа объекта, обратите внимание на то, что атрибуты ключевых слов имеют тип набора Keyword_tab_t.
CREATE TYPE Image_t AS OBJECT (
image_id INTEGER,
file_name VARCHAR2(512),
file_type VARCHAR2(12),
bytes INTEGER,
keywords Keyword_tab_t,
MEMBER FUNCTION set_attrs (new_file_name IN VARCHAR2,
new_file_type IN VARCHAR2, new_bytes IN INTEGER)
RETURN Image_t,
MEMBER FUNCTION set_keywords (new_keywords IN Keyword_tab_t)
RETURN Image_t,
PRAGMA RESTRICT_REFERENCES (DEFAULT, RNDS, WNDS, RNPS, WNPS)
);
Ниже приводится тело:
CREATE TYPE BODY Image_t
AS
MEMBER FUNCTION set_attrs (new_file_name IN VARCHAR2,
new_file_type IN VARCHAR2, new_bytes IN INTEGER)
RETURN Image_t
IS
image_holder Image_t := SELF;
BEGIN
image_holder.file_name := new_file_name;
image_holder.file_type := new_file_type;
image_holder.bytes := new_bytes;
RETURN image_holder;
END;
MEMBER FUNCTION set_keywords (new_keywords IN Keyword_tab_t)
RETURN Image_t
IS
image_holder Image_t := SELF;
BEGIN
image_holder.keywords := new_keywords;
RETURN image_holder;
END;
END;
В данный момент не существует связи между реляционными таблицами и объектным типом. Это независимые организмы. Теперь нужно построить объектное представление, с помощью которого объектное описание "накладывается" на таблицы.
Для создания объектного представления используется следующее выражение:
CREATE VIEW images_v
OF Image_t
WITH OBJECT OID (image_id)
AS
SELECT i.image_id, i.file_name, i.file_type, i.bytes,
CAST (MULTISET (SELECT keyword
FROM keywords k
WHERE k.image_id = i.image_id)
AS Keyword_tab_t)
FROM images i;
(Согласно документации Oracle это выражение синтаксически верно. К сожалению, это выражение не будет скомпилировано в Oracle 8.0.3. Выход состоит в создании пакетной функции, которая принимает image_id и возвращает набор типа Keyword_tab_t, и использовании этой функции в выражении SELECT, приведенном выше. Более подробно в [Feuer97]).
Интересно, что существует только пара компонент этого выражения, являющихся уникальными для объектного представления: OF Image_t означает, что представление будет возвращать объект типа Image_t. WITH OBJECT OID (image_id) позволяет вам определить первичный ключ в качестве основы для виртуального OID. Это позволяет создавать ссылки на виртуальные объекты.
Предложение CAST…, приведенное выше, может быть использовано в любом представлении, не только объектном (но для этого необходимо наличие объектной опции Oracle). Этот подзапрос выполняет преобразование подчиненных (detail) записей в тип набора "на лету". (Более подробная информация об операторах CAST и MULTISET, а также примеры, приведена в [Feuer97].
Различия между объектными представлениями и объектными таблицами.
Кроме очевидных различий между представлением и таблицей, существуют еще более тонкие различия между объектным представлением и объектной таблицей. Эти различия распространяются на:
Уникальность идентификатора объекта. В то время как объектная таблица всегда имеет уникальный OID, имеется возможность создать объектные представления, содержащие дублирующиеся виртуальные идентификаторы объектов.
Использование ссылок. В объектных таблицах имеется возможность использовать ссылку, указывающую на экземпляр объектного представления. Однако, эти ссылки должны быть созданы из внешних ключей с помощью специальной встроенной функции MAKE_REF, которая оказывается чересчур чувствительной. В частности, если внешний ключ имеет значение null, MAKE_REF возвращает ошибку!
Хранение ссылок. Объектные таблицы могут хранить значения ссылок постоянно, но ссылки на виртуальные объекты в объектных представлениях должны создаваться динамически (с помощью MAKE_REF).
Ссылки на неуникальные идентификаторы объектов (OID). В объектных таблицах невозможно использовать ссылки на неуникальные идентификаторы объектов; но при использовании объектного представления, имеющего такие ссылки, может возникнуть такая проблема. Необходимо принять это во внимание и разрешить ее, если необходимо.
Стратегия для объектных представлений.
Вот несколько фактов, которые помогут вам при работе с объектными представлениями:
Объектные представления могут предоставить определенные преимущества по сравнению с традиционными объектами, поскольку они допускают изменения схемы. Не выяснена возможность появления коллизий выполнения, которые при этом могут возникать (по сравнению с объектными таблицами).
Будьте осторожны при определении SELECT в объектном представлении, которое использует MAKE_REF. Следует избегать возможности передачи MAKE_REF значения null.
Объявите OID объектного представления уникальным, если не существует веской причины, чтобы сделать иначе.
Выберите постоянный подход к ограничению использования DML (язык управления данными) в объектных представлениях. Триггеры INSTEAD OF могут проявить запутанные взаимодействия друг с другом. Оптимальной конструкцией могут стать пакеты, в которых описывается логика вставки, обновления и удаления.
Оценка
Этот материал не претендует на простоту для начинающих, и сложность эта гораздо глубже, чем просто синтаксическая. Кроме операционных ограничений, которые мы обсудили, само понятие "думающего объекта" сложно принять программистам, прошедшим школу баз данных или стуктурированного подхода. Что же руководит многими организациями, выдвигающими объектный подход на первый план? Решение основной задачи руководителей, казавшееся мечтой, повторное использование вместо повторного создания программного обеспечения, необходимо для управления их бизнессом [Jacob96]. В промышленности, где готовые решения не удовлетворяли потребности автоматизации, менеджеры постоянно были вынуждены разрабатывать все новые и новые решения для поддержки существующего кода, пытаясь при этом удержать стоимость разработки под контролем.
Возможно, из наших примеров не очевидно, каким образом объектная опция ведет к возможности повторного использования. Частично это происходит из-за проблем, возникающих при изменении схемы, и из-за недостаточно развитого наследования в Oracle 8.0. В самом деле, преимущества объектного подхода не достаются практику автоматически; в частности, большие системы должны показывать другие характеристики [Booch94]. Достижение повторного использования требует тщательного планирования и обдуманного исполнения.
Эксперты не рекомендуют пробовать объектный подход только потому, что кто-то сказал, что он удобен, или потому, что кто-либо еще использует его. При отсутствии времени и финансирования для того, чтобы разобраться в проблеме, и без использования преимуществ различных программных моделей вряд ли можно получить хороший результат.
Почти наверняка, корпорация Oracle будет добавлять необходимые свойства, такие как наследование и инструменты для развития схемы в объектную опцию. Однажды, объекты станут стандартной частью сервера. Пока же технология только развивается, ранние преемники будут наслаждаться удовольствием поиска сфер применения, и будут достигать глубокого понимания особенностей, которые позднее появятся в продукте.
Благодарности.
Авторы выражают свою признательность Дональду Херкимеру из корпорации Oracle за поддержку во время подготовки рукописи этой статьи.
Ссылки.
[Booch94] Booch, Grady. Object-Oriented Analysis and Design with Applications, Benjamin/Cummings, 1994.
[Feuer97] Feuerstein, Steven, with Bill Pribyl. Oracle PL/SQL Programming, O' Reilly & Associates, 1997.
[Jacob96] Jacobson, Ivar. " Reuse in Reality: The Reuse-Driven Software-Engineering Business." Представлено на Object Expo в Париже, доступно по адресу: http://www.rational.com/support/techpapers/objex_ivar.pdf.
|