- •20.7. Средства sql
- •20.8. Резюме
- •21.1. Введение
- •21.2. Некоторые аспекты технологам поддержки принятия решений
- •21.3. Проектирование базы данных поддержки принятия решений
- •21.5. Хранилища данных и магазины данных
- •21.6. Оперативная аналитическая обработка
- •21.7. Разработка данных
- •21.8. Резюме
- •22.1. Введение
- •22.2. Хронологические данные
- •22.3. Основная проблема хронологических баз данных
- •22.4. Интервалы
- •22.5. Интервальные типы
- •22.6. Скалярные операторы для интервалов
- •22.7. Операторы обобщения для интервалов
- •22.8. Реляционные операторы для обработки интервалов
- •22.9. Ограничения, включающие интервалы
- •22.10. Операторы обновления, включающие интервалы
- •22.11. Проектирование базы данных
- •22.12. Резюме
- •23.1. Введение
- •23.2. Обзор основных концепций
- •23.3. Исчисление высказываний
- •23.4. Исчисление предикатов
- •23.5. Базы данных с точки зрения доказательно-теоретического подхода
- •23.6. Дедуктивные субд
- •23.7. Обработка рекурсивных запросов
- •23.8. Резюме
- •Часть VI
- •24.1. Введение
- •24.2. Объекты, классы, методы и сообщения
- •24.3. Еще раз об объектах и объектных классах
- •Cdo для класса set (ref(emp))
- •24.4. Простой пример
- •1 | Course с001 , с001 0ffs , с001 ny offs |
- •24.5. Дополнительные аспекты
- •24.6. Резюме
- •25.1. Введение
- •X2 rational, y2 rational ) ... ;
- •25.2. Первая грубейшая ошибка
- •25.3. Вторая грубейшая ошибка
- •25.4. Вопросы реализации
- •25.5. Преимущества реального сближения двух технологий
- •25.6. Резюме
24.4. Простой пример
В предыдущей главе были представлены базовые концепции объектного подхода. В данной главе на простом примере последовательно демонстрируется применение этих идей на практике, а именно — как определяется объектная база данных, как она пополняется данными, как в ней выполняются операции извлечения и обновления данных. В рассматриваемом примере используются объектная СУБД GemStone (разработка корпорации GemStone Systems) и ее язык запросов OPAL [24.14]. Язык OPAL, в свою очередь, основывается на языке Smalltalk [24.26], одном из наиболее истинно объектных языков (поэтому он здесь и используется).
Замечание Необходимо добавить, что язык Smalltalk появился на рынке раньше, чем язык С++, и гораздо раньше, чем язык Java. Однако несмотря на это два последних языка являются менее "чистыми" объектными языками, чем Smalltalk.
В качестве примера воспользуемся упрощенной версией базы данных профессиональной подготовки из упр. 8.10 главы 8. В этой базе данных содержится информация о схеме подготовки и обучения специалистов внутри некоторой компании. Для каждого курса обучения (COURSE) в базе данных содержится описание отдельных потоков, организованных для его изучения (OFFERING). Для каждого потока хранятся данные обо всех его слушателях (ENROLLMENT) и преподавателях (TEACHER). Кроме того, в базе содержатся сведения о сотрудниках компании. Реляционную версию базы данных можно описать следующим образом.
COURSE
OFFERING
TEACHER
ENROLLMENT EMP
COURSE!, TITLE }
COURSE!, OFF!, OFFDATE, LOCATION } COURSE!, OFF!, EMP! } COURSE!, OFF*, EMP!, GRADE } EMP!, ENAME, SALARY, POSITION }
На рис. 24.6 показана диаграмма связей для рассматриваемой базы данных. Более подробно она описана в главе 8, в упражнениях и ответах к упражнениям.
COURSE
OFFERING
ENROLLMENT
TEACHER
EMPLOYEE
Рис. 24.6. Диаграмма связей для образовательной базы данных
Определение данных
Теперь перейдем к определению данных на языке OPAL для образовательной базы данных. Ниже приводится первое определение объектного класса ЕМР, описывающего сотрудников (для удобства строки этого определения пронумерованы).
1 OBJECT SUBCLASS : 'ЕМР'
INSTVARNAMES : f[ 'EMPf', 'ENAME', 'POSITION' ]
CONSTRAINTS : f[ f[ IEMPf, STRING ] ,
[ iENAME, STRING ],
[ IPOSITION, STRING ] ] .
Пояснения. В строке 1 определен объектный класс ЕМР, производный от встроенного класса OBJECT. (Согласно терминологии языка OPAL в строке 1 отсылается сообщение объекту OBJECT с запросом выполнить метод SUBCLASS, причем с помощью предложений INSTVARNAMES и CONSTRAINTS указываются параметры вызова этого метода. Для определения нового класса (как и всего прочего) необходимо отослать сообщение объекту.) В строке 2 указывается, что объекты класса ЕМР имеют три закрытые переменные экземпляра— EMPf, ENAME и POSITION, а в строках 3-5 на эти переменные экземпляра накладываются ограничения, указывающие, что они должны содержать объекты класса STRING.
Замечание. В настоящей главе опускаются чисто синтаксические подробности, несущественные для преследуемых нами целей (например, вездесущие знаки присутствующие в приведенном выше примере).
Подчеркнем, что переменные экземпляра EMPf, ENAME и POSITION — закрытые переменные для класса ЕМР, поэтому доступ к ним по именам допустим только в методах этого класса. В качестве примера ниже даны определения методов "получить и установить", т.е. методов, позволяющих выбрать и обновить номера служащих (здесь символ "А" можно читать как "возвратить").
METHOD : ЕМР GET EMPf ~ЕМР#
I
METHOD : EMP
SET EMPi : EMPi PARM ~EMP| := EMP? PARM
%
В следующем подразделе о методах определения мы поговорим подробнее. А сейчас рассмотрим определение класса описания курса COURSE.
OBJECT SUBCLASS INSTVARNAMES CONSTRAINTS
'COURSE'
t[ 'COURSE!', 'TITLE', #[ #[ !C0URSE!, STRING [ !TITLE, STRING ] [ iOFFERINGS, OSET
'OFFERINGS' ]
Пояснения. В строке 5 определена переменная OFFERINGS, которая содержит объект класса 0SET, или, точнее, идентификатор объекта класса 0SET. Выражаясь неформально, переменная OFFERINGS обозначает множество всех потоков для данного курса. Иначе говоря, связь "курс — поток" моделируется с помощью иерархии вложения, в которой потоки концептуально содержатся внутри соответствующего курса. Определение класса 0SET будет дано ниже, а определение класса потока OFFERING может быть записано так.
OBJECT SUBCLASS INSTVARNAMES
'OFFERING' ![ 'OFF!'
CONSTRAINTS
'ODATE', 'LOCATION', 'ENROLLMENTS', 'TEACHERS' ] ![ !0FF!, STRING ] , [ !0DATE, DATETIME ], [ LOCATION, STRING ], [ !ENROLLMENTS, NSET ], 8 [ !TEACHERS, TSET ] ] .
1 OBJECT
SUBCLASS
INSTVARNAMES
CONSTRAINTS
4
'ENROLLMENT' ![ 'ЕМР', 'GRADE' ] ♦[ |[ «ЕМР, ЕМР ] ,
[ IGRADE, STRING ] ].
Пояснения. В строке 3 определяется закрытая переменная экземпляра ЕМР, содержащая идентификатор объекта класса ЕМР, представляющий отдельного сотрудника, который является слушателем курса.
Замечание. Чтобы продолжить иерархию вложения, объект ЕМР помещается в объект ENROLLMENT. Однако здесь можно заметить асимметрию: зачисление сотрудника на обучение в разные потоки описывается как отношение типа "многие ко многим", однако члены этого отношения, сотрудники и потоки, обрабатываются совершенно по-разному.
И наконец рассмотрим объекты, представляющие преподавателей. В нашем примере следует несколько отвлечься от исходной реляционной версии базы данных и рассмотреть преподавателей (TEACHER) как подкласс класса сотрудников (ЕМР).
1 ЕМР SUBCLASS : 'TEACHER'
INSTVARNAMES : f[ 'COURSES' ]
CONSTRAINTS : f[ §[ ICOURSES, CSET ] ] .
Пояснения. В строке 1 определен объектный класс TEACHER, который является подклассом ранее определенного класса ЕМР (иначе говоря, TEACHER "ISA" ЕМР). Таким образом, каждый отдельный объект TEACHER имеет закрытые переменные экземпляра EMPf, ENAME и POSITION (которые унаследованы от класса ЕМР8), а также переменную COURSES, которая содержит идентификатор объекта класса CSET. Объект CSET определяет множество всех курсов, которые ведет конкретный преподаватель. Каждый объект TEACHER также наследует все методы класса ЕМР.
Как уже отмечалось, в предыдущих определениях классов предполагалось существование нескольких классов коллекций (например, коллекций объектов потоков, учеников, преподавателей и т.д.), но они не были определены. Теперь следует дать точное определение коллекции каждого класса, а именно: ESET, CSET, OSET, NSET и TSET. Например, объект класса CSET будет состоять из множества идентификаторов отдельных объектов класса COURSE. Ниже даются определения всех этих классов, начиная с класса ESET.
1 SET SUBCLASS : 'ESET'
2 CONSTRAINTS : EMP .
Пояснения. В строке 1 дается определение класса ESET, который является подклассом встроенного класса SET. В строке 2 на объекты класса ESET накладывается ограничение: они должны быть множествами идентификаторов объектов класса ЕМР. В общем случае может существовать произвольное число объектов класса ESET, но в данной ситуации будет создан только один объект (подробности приводятся в следующем подразделе), который будет содержать множество идентификаторов всех объектов класса ЕМР. Выражаясь неформально, объект ESET можно считать аналогом базовой переменной-отношения ЕМР в реляционной версии базы данных.
Определения классов CSET, OSET, NSET и TSET аналогичны (они приводятся ниже). Однако для каждого из них придется создать не один, а несколько объектов соответствующей коллекции классов. Например, в нашем случае будет существовать столько коллекций объектов OSET, сколько существует отдельных объектов COURSE.
SET SUBCLASS : 'CSET' CONSTRAINTS : COURSE .
SET SUBCLASS : 'OSET'
CONSTRAINTS : OFFERING .
SET SUBCLASS : 'NSET'
CONSTRAINTS : ENROLLMENT .
Заметим, что здесь наследуется закрытое представление (т.е. физическая реализация).
SET SUBCLASS : 'TSET' CONSTRAINTS : TEACHER .
Заполнение базы данных
Теперь обсудим, как можно поместить в рассматриваемую базу данных требуемую информацию. При этом остановимся на пяти основных типах объектов (сотрудники, курсы, потоки, слушатели, преподаватели). Начнем с сотрудников. Напомним о нашем намерении собрать вместе идентификаторы всех объектов ЕМР в единственном объекте ESET. Таким образом, прежде всего необходимо создать объект ESET.
0ID_0F_SET_0F_ALL_EMPS := ESET NEW .
Правая часть этого выражения возвращает объектный идентификатор (OID) нового пустого экземпляра объекта класса ESET (т.е. пустое множество идентификаторов объектов класса ЕМР), а затем идентификатор этого нового экземпляра присваивается программной переменной 0ID_0F_SET OF_ALL_EMPS. Говоря неформально, эта переменная обозначает множество всех сотрудников.
Теперь всякий раз при создании нового объекта класса ЕМР идентификатор этого объекта следует помещать в объект ESET, идентификатор которого хранится в переменной 0ID_0F_SET_0F_ALL_EMPS. Для создания объекта класса ЕМР и вставки его идентификатора в объект класса ESET необходимо определить метод или же написать программу, которая выполнит эту задачу.
1 METHOD : ESET " анонимный! "
ADD EMPI : EMPI PARM " параметры "
ADD~ENAME : ENAME PARM
ADD~P0S : P0S_PARM
j EMP_0ID I " локальная переменная "
EMP OID := EMP NEW . " новый сотрудник "
EMP~0ID SET EMPI : EMPi PARM ; " инициализация "
8 ~ SET~ENAME : ENAME PARM ;
9 SET~P0S : POS PARM .
10 SELF ADD: EMP OID . ~ " вставка "
11 %
Пояснения
В строке 1 начинается запись кода данного метода (который завершается символом "%" в строке 11) для объектов класса ESET. (На самом деле в системе во время выполнения программы будет существовать всего один объект этого класса.)
В строках 2-4 определены три параметра с внешними именами ADD_EMPI, ADD_ENAME и ADD_P0S. Эти имена будут использованы в сообщениях, вызывающих данный метод. Соответствующие внутренние имена EMPf_PARM, ENAME_PARM и P0S_PARM будут использоваться только внутри кода реализации данного метода.
В строке 5 определена локальная переменная EMP_0ID, а в строке 6 ей присвоен идентификатор нового неинициализированного экземпляра объекта класса ЕМР.
В строках 7-9 посылается сообщение новому объекту класса ЕМР с указанием трех методов (SET_EMP#, SETJENAME и SET_P0S) и передачей одного аргумента каждому из них (EMPI PARM для SET~EMP|, ENAME PARM для SET ENAME и POS PARM для SET POS).
Замечание. Здесь предполагается, что SET_EMPl является методом, который используется для объекта класса ЕМР и предназначается для присвоения некоторого значения переменной EMPf. Аналогично определяются методы SETJ2NAME и SET_P0S. Несколько подробнее эти методы рассматриваются ниже.
■ В строке 10 посылается сообщение объекту SELF. Это имя объекта является специальным обозначением, представляющим во время выполнения тот самый текущий объект, в котором определен указанный метод (т.е. сообщение посылается самому текущему целевому объекту). Сообщение вызывает встроенный метод ADD (этот метод содержится в каждом классе, определяющем "коллекцию"). В результате идентификатор объекта, который содержится в локальной переменной EMP_0ID, будет вставлен в объект, идентифицируемый значением переменной SELF (в данном случае это будет объект ESET, содержащий идентификаторы всех существующих на текущий момент объектов класса ЕМР).
Замечание. Переменная SELF необходима потому, что параметр, соответствующий объекту-получателю, не имеет собственного имени (см. строку 1).
■ Обратите внимание, что, как отмечено в комментарии к строке 1, определенный здесь метод остается анонимным. В общем случае в языке OPAL методы не имеют имен; вместо этого для них используется сигнатура (т.е. комбинация имени класса, в котором он применяется, и внешних имен их параметров). Однако при этом возникает опасность создания неуклюжих и огромных выражений. Можно отметить и еще один недостаток: если два метода одного и того же класса используют одни и те же параметры, то для этих методов должны использоваться разные внешние параметры.
Теперь новый метод, созданный для вставки в базу данных новых объектов класса ЕМР, можно применить следующим образом.
OID OF SET OF ALL EMPS ADD EMPf : 'E009'
ADD~ENAME : 'Helms' ADD_P0S : 'Janitor' .
При использовании приведенного выражения будет создан объект класса ЕМР для сотрудника с номером 'Е009', а идентификатор этого объекта будет добавлен к множеству всех идентификаторов объектов класса ЕМР.
Обратите внимание, что встроенный метод NEW никогда не должен использоваться для класса ЕМР, кроме тех случаев, когда он является частью только что определенного метода. Иначе могут быть созданы лишние объекты класса ЕМР, которые не будут представлены в "множестве всех сотрудников".
Замечание. Следует принести извинения за слишком частое повторение таких выражений, как "множество всех сотрудников" и "только что определенный метод", однако сложно описывать то, что не имеет собственного имени.
А теперь займемся курсами. Объекты для сотрудников представляют собой наиболее простой случай, поскольку они соответствуют "регулярным объектам" (согласно терминологии модели типа "сущность/связь") и не содержат никаких других объектов (не считая неизменяемых). Далее следует рассмотреть более сложный случай объектов для курсов, которые (несмотря на то, что они все еще остаются "регулярными объектами") концептуально содержат другие изменяемые объекты. В целом, чтобы создать объекты для курсов, необходимо выполнить следующие действия.
Применить метод NEW для класса CSET с целью создания исходно пустого "множества всех курсов" (на самом деле — множества идентификаторов объектов класса COURSE).
Определить метод для создания нового объекта класса COURSE и вставки его идентификатора в "множество всех курсов". При создании этого объекта потребуется задать некоторые значения для параметров COURSE! и TITLE. Кроме того, с помощью метода NEW класса 0SET потребуется также создать исходно пустое множество потоков (на самом деле — идентификаторов объектов класса OFFERING), а затем разместить идентификатор этого пустого множества потоков в переменной 0FERINGS внутри нового объекта класса COURSE.
Вызвать только что определенный метод для каждого отдельного курса.
Теперь создадим объекты потоков. Для этого необходимо выполнить следующие действия.
1. Определить метод для создания нового объекта класса OFFERING. Этот метод должен принимать в качестве параметров значения переменных OFFt, 0DATE и LOCATION и приводить к созданию нового объекта класса OFFERING с указанными параметрами. Кроме того, потребуется выполнить некоторые дополнительные действия.
» Для создания исходно пустого множества слушателей (идентификаторов объектов класса ENROLLMENT) следует применить метод NEW для класса NSET, а затем разместить идентификатор этого пустого множества слушателей в переменной ENROLLMENTS внутри нового объекта класса OFFERING.
■ Для создания исходно пустого множества преподавателей (идентификаторов объектов класса TEACHER) следует применить метод NEW для класса NSET, а затем разместить идентификатор этого пустого множества преподавателей в переменной TEACHERS внутри нового объекта класса OFFERING.
2. Данный метод принимает в качестве параметра значение переменной COURSE! и применяет его следующим образом.
■ Используя указанное значение параметра, необходимо найти объект класса COURSE, соответствующий новому объекту класса OFFERING. Как это можно выполнить, описано в следующем подразделе.
Замечание. Безусловно, такой метод не позволит создать объект для нового потока, если не будет найден соответствующий ему курс обучения. Далее в этой главе такие исключительные случаи рассматриваться не будут.
Следует найти "множество всех потоков" для данного объекта класса COURSE.
Добавить идентификатор нового объекта класса OFFERING к соответствующему "множеству всех потоков".
Заметьте, что (как уже упоминалось в этой главе) здесь нельзя избежать применения таких пользовательских ключей, как COURSE!. Действительно, они необходимы не только для отображения объектов окружающего мира, но и для организации подстановок справочных данных внутри базы данных.
3. Наконец следует вызвать только что определенный метод для каждого отдель- ного потока.
Обратите внимание, что (в соответствии с используемым представлением иерархии вложения) здесь не было создано "множество всех потоков". В результате для выполнения запроса, в котором данные коллекции используются в качестве анализируемой области (например, для запроса "Найти все потоки, преподаваемые в Нью-Йорке"), потребуется дополнительно создать некоторую процедуру (см. следующий подраздел).
Теперь рассмотрим процедуру создания объектов слушателей. Эти объекты (ENROLLMENT) отличаются от объектов потоков тем, что содержат переменную экземпляра ЕМР, значение которой является идентификатором соответствующего объекта ЕМР. Поэтому последовательность действий должна быть такой.
1. Определить метод для создания нового объекта класса ENROLLMENT. В этом методе в качестве аргументов используются значения переменных COURSEI, OFFf, EMPI и GRADE и создается новый объект ENROLLMENT с заданным значением аргумента GRADE. Кроме того, требуется выполнить некоторые дополнительные действия.
Использовать значения COURSE! и OFFf для нахождения объекта OFFERING, соответствующего новому объекту класса ENROLLMENT.
Найти "множество всех слушателей" для данного объекта класса OFFERING.
Добавить идентификатор нового объекта класса ENROLLMENT к соответствующему "множеству всех слушателей".
Кроме того, потребуется следующее.
Использовать значение переменной EMPf для нахождения соответствующего объекта ЕМР.
Разместить идентификатор объекта ЕМР в переменной ЕМР внутри нового объекта класса ENROLLMENT.
2. Вызывать только что определенный метод для каждого отдельного потока.
И наконец перейдем к созданию объектов преподавателей. Различие между способами создания объектов для преподавателей и потоков заключается в том, что класс TEACHER является подклассом класса ЕМР. Ниже приведена последовательность действий, которые необходимо выполнить в данном случае.
1. Определить метод создания нового объекта класса TEACHER. Этот метод принимает в качестве аргументов значения переменных COURSE!, OFFf и EMPf. Потребуются также некоторые дополнительные действия.
Использовать значение переменной EMPf для нахождения соответствующего объекта класса ЕМР.
Преобразовать объект класса ЕМР в объект класса TEACHER (поскольку этот сотрудник теперь является также преподавателем). Специфика подобного преобразования зависит от конкретной системы (см. главу 19), и здесь этот вопрос рассматриваться не будет.
Использовать значения переменных COURSEf и OFFf для поиска объекта класса OFFERING, соответствующего новому объекту класса TEACHER.
Найти "множество всех преподавателей" для этого объекта OFFERING.
■ Добавить идентификатор нового объекта класса teacher к соответствующему "множеству всех преподавателей".
Определить множество всех курсов, которые может вести данный преподаватель, а также задать соответствующим образом значение переменной courses в новом объекте класса teacher. Однако здесь эти подробности будут опущены.
Вызывать только что определенный метод для каждого объекта, описывающего отдельного преподавателя.
Операции извлечения
Прежде чем приступить к подробному описанию операций извлечения, следует отметить, что (хотя это и вполне очевидно) язык OPAL, как и другие объектные языки в целом, функционирует по принципу последовательной обработки отдельных записей, а не их множеств. Следовательно, для решения большинства проблем программист должен создать некоторую процедуру.
Рассмотрим в качестве примера запрос "Найти все потоки для курса с номером 'с001', которые преподаются в городе 'New York'". Для простоты предположим, что есть переменная 00s0ac, значение которой является идентификатором "набора всех курсов". Ниже приводится код для такого запроса.
