Объектно-ориентированные СУБД. ODMG = Object Data Management Group: разработка стандарта для объектно-ориентированных СУБД ODL = Object Definition Language: объектноориентированный подход к разработке БД. OQL = Object Query Language: запросы к ООБД со структурой, описываемой ODL в виде подобном SQL. Краткий обзор ODL. Объявление класса включает: 1. Имя класса. 2. Объявление ключей (необязательное). 3. Объявление пространства (extent) = имя для множества текущих объектов класса 4. Объявления элементов. Элемент является аттрибутом, отношением или методом. Объявление классов в ODL. class <name> { elements = attributes, relationships, methods } Объявление элементов. attribute <type> <name>; relationship <rangetype> <name>; Отношения(связи) определяются между объектами; аттрибуты используют не-объектные значения, например, целые числа. Пример метода. float gpa(in string) raises(noGrades) float = тип возвращаемого значения. in: указывает режим использования аргумента (предположительно, имени студента) – только-чтение, другие режимы: out, inout. noGrades является исключением, которое может быть выставлено методом gpa. Отношения (связи) ODL. Поддерживаются только бинарные отношения (связи) между объектами. Множественные отношения(связи) требуют дополнительно поддерживающего (связующего) класса, как обсуждалось для E/R моделей. Связи между классами объектов описываются взаимообратными парами. Например, связь Sells между beers и bars представлено связью в bars, где представляет сорта продаваемого в баре пива, и связью в beers, где предсталяет множество баров, в которых продается данный сорт пива. Связь «многие-к-многим» имеет тип множество (коллекция) в обоих направлениях. Связь «многие-к-одному» имеет тип множество на стороне «одного» и простое имя класса на стороне «многих». Связь «один-к-одному» имеет в качестве типа простое имя класса в обоих направлениях. Пример. class Beers { attribute string name; attribute string manf; relationship Set<Bars> servedAt inverse Bars::serves; relationship Set<Drinkers> fans inverse Drinkers::likes; } Элемент из другого класса указывается при помощи <имя_класса>::<имя_элемента> Множественный тип образуется при помощи Set<имя_типа>. class Bars { attribute string name; attribute Struct Addr {string street, string city, int zip} address; attribute Enum Lic {full, beer, none} licenseType; relationship Set<Drinkers> customers inverse Drinkers::frequents; relationship Set<Beers> serves inverse Beers::servedAt; } Структурированные типы имеют имя и список пар типаттрибут, заключенный в фигурные скобки. Перечислимый тип имеет имя и список значений, заключенный в фигурные скобки. class Drinkers { attribute string name; attribute Struct Bars::Addr address; relationship Set<Beers> likes inverse Beers::fans; relationship Set<Bars> frequents inverse Bars::customers; } Заметьте повторное использование типа Addr. Система типов в ODL. Базовые типы: int, real/float, string, перечислимые типы и классы. Конструкторы типов: Struct – для структур for structuresб и 5 типов коллекций: Set, Bag, List, Array, Dictionary. Тип связи может быть классом или коллекцией класса. Связь «многие_к_одному». Не используйте коллекцию для связи на стороне «многие». Пример. Drinkers имеют любимое пиво. class Drinkers { attribute string name; attribute Struct Bars::Addr address; relationship Set<Beers> likes inverse Beers::fans; relationship Beers favoriteBeer inverse Beers::realFans; relationship Set<Bars> frequents inverse Bars::customers; } Также нужно добавить к Beers: relationship Set<Drinkers> realFans inverse Drinkers::favoriteBeer; Пример множественных связей. Рассмотрим тернарную связь bars-beers-prices. Мы должны создать связующий класс BBP. class Prices { attribute real price; relationship Set<BBP> toBBP inverse BBP::thePrice; } class BBP { relationship Bars theBar inverse ... relationship Beers theBeer inverse ... relationship Prices thePrice inverse Prices::toBBP; } Обратные связи для theBar, theBeer должны быть добавлены к Bars, Beers. В данном конкретном случае лучше было бы не создавать класс Prices, а сделать price аттрибутом BBP. Заметьте, что ключи не обязательны.Notice that keys are optional. BBP не имеет ключей, но не является здесь слабым. Существующий внутренний идентификатор объекта достаточен для различения объектов класса BBP. Роли в ODL. Имена связей описывают роли в них объектов. Пример. Супруги и друзья-посетители. class Drinkers { attribute string name; attribute Struct Bars::Addr address; relationship Set<Beers> likes inverse Beers::fans; relationship Set<Bars> frequents inverse Bars::customers; relationship Drinkers husband inverse wife; relationship Drinkers wife inverse husband; relationship Set<Drinkers> buddies inverse buddies; } Заметьте, что указание класса Drinkeкs:: является не обязательным, когда обратная связь задана на том же классе. Подклассы в ODL. При описании подкласса после его имени указывается имя надкласса(суперкласса), отделенное от имени подкласса двоеточием. Пример. Эль является пивом с дополнительным аттрибутом – цветом. class Ales:Beers { attribute string color; } Объекты класса Ales наследуют все аттрибуты и методы класса Beers. В то время как E/R сущности могут присутствовать и в классе и в подклассе, в ODL предполагается, что каждвй объект является членом только одного класса. Ключи в ODL. Указываются при помощи ключевого слова key(или keys) и списка аттрибутов, образующих ключ. Описание ключа помещается в скобках после имени класса вместе с описанием пространства объектов(экстента). Можно указать несколько списков аттрибутов, описывающих альтернативные ключи. Скобки могут использоваться для группирования аттрибутов одного ключа Так, например, (key(a1, а2, ..., an )) означает один ключ, состоящий из всех n аттрибутов, для (key a1, а2, ..., an ) – каждый аi является отдельным ключом. Пример. class Beers (key name) { attribute string name ... ... } Помните: ключи не являются обязательными в ODL. Внутренний идентификатор (ID) объекта достаточен для различения объектов, имеющих одинаковые значения атрибутов. Пример: несколько многоаттрибутных ключей. class Courses (keys (dept, number), (room, hours)) { ... } Перевод ODL в отношения. 1. Классы без связей переводятся в отношения также как множества сущностей, однако при этом появляется несколко новых проблем. 2. Классы со связями: a) Рассматривать связи отдельно, как для E/R диаграмм. b) Присоединить связь « многие-к-одному» к отношению для «многих». Классы ODL без связей. Проблема: ODL позволяет создавать типы аттрибутов на основе структур и коллекций. Для структуры: создается один аттрибут для каждого элемента структуры. Множество(Set): создается один кортеж для каждого элемента множества. Что, если имеется более одного множества среди аттрибутов класса? В таком случае создается один кортеж для каждой комбинации элементов из множеств. Проблема: ODL класс может не иметь ключей, но мы должны иметь ключ в отношении для пердсталения идентификатора объекта. Пример. class Drinkers (key name) { attribute string name; attribute Struct Addr { string street, string city, int zip } address; attribute Set<string> phone; } name street city zip phone n1 s1 c1 z1 p1 n1 s1 c1 z1 p2 ... Сюрприз: ключ для класса (name) не является ключом для отношения (name, phone). name в классе определяет уникальный объект, включая множество телефонов. name в отношении не определяет единственным образом кортеж. Поскольку кортежи не являются идентичными объектам класса, то это не может считаться несогласованностью Нарушение НФБК: необходимо отделить name-phone. ODL связи. Если связь – «многие-к-одному» от А к В, поместить ключ класса B в отношение для класа A. Если связь – «многие-к-многим», мы вынуждены дублировать A-кортежи как в случае объекта ODL с аттрибутом типа множество. Было бы удобнее создать отдельное отношения для связи «многие-к-многим»? В любом случае мы бы пришли к этому в ходе НФБК декомпозиции. Пример. class Drinkers (key name) { attribute string name; attribute string addr; relationship Set<Beers> likes inverse Beers::fans; relationship Beers favorite inverse Beers::realFans; relationship Drinkers husband inverse wife; relationship Drinkers wife inverse husband; relationship Set<Drinkers> buddies inverse buddies; } Drinkers(name, addr, beerName, favBeer, wife,buddy) Проведем декомпозицию к 4НФ ФЗ: name –> addr favBeer wife MФЗ: name ->->beerName, name ->-> buddy Результат декомпозиции: Drinkers(name, addr, favBeer, wife) DrBeer(name, beer) DrBuddy(name, buddy) Объектный язык запросов (OQL). Мотивировка. Реляционные явыки страдают от несоответствия потоков данных для их связи с обычными явыками типа C или C++. Модели данных, используемые C и SQL, совершенно отличны, например, C не имеет отношений, множеств или наборов в качестве своих примитивных типов; C можно назвать обрабатывающим кортеж-в-один-момент, SQL – отношение-водин-момент. OQL является попыткой OO сообщества пользователей расширить язвки типа C++ SQL-типа отношение-в-одинмомент возможностями. OQL типы. Базовые типы: string, int, real, и т.д. плюс имена классов. Конструкторы типов: Struct для структур. Коллекции: set, bag, list, array. Подобно ODL, но нет ограничений на количество применений конструктора типов. Set(Struct()) и Bag(Struct()) выполняют специальную роль подобно отношениям. OQL использует ODL в части определения схемы. Для каждого класса мы можем определить пространство объектов(extent) = имя для текущего множества объектов класса. Необходимо ссылаться на пространство объектов (не имя класса) в запросах к базе данных. Пример. class Bar (extent Bars) { attribute string name; attribute string addr; relationship Set<Sell> beersSold inverse Sell::bar; } class Beer (extent Beers) { attribute string name; attribute string manf; relationship Set<Sell> soldBy inverse Sell::beer; } class Sell (extent Sells) { attribute float price; relationship Bar bar inverse Bar::beersSold; relationship Beer beer inverse Beer::soldBy; } Выражения для доступа к объектам и аттрибутам. Пусть x – объект класса C. Если a - аттрибут C, то x.a = значение аттрибута а для объекта х. Если r – связь класса C, то x.r = значение, с которым x связан через r. Это может быть объек или коллекция объектов в зависимости от типа r. Если m – метод класса C, то x.m(...) является результатом применения m к объекту x. Примеры. Пусть s – переменная типа Sell. s.price = значение price для объекта s. s.bar.addr = адрес бара используемого в s. Замечание: повторное использование . («точки») допустимо, поскольку s.bar является объектом, а не коллекцией. Пример недопустимого использования «точки». b.beersSold.price, где b – объект типа Bar. Почему недопустимо? Потому что b.beersSold является множеством объектов, а не отдельным объектом. OQL Select-From-Where. SELECT <список значений> FROM <список коллекций и типичных элементов> WHERE <условие> Коллекции в списке FROM могут быть: 1. Пространством объектов(extent). 2. Выражением со значением-коллекцией. За коллекцией идет имя типичного элемента(по аналогии с переменной-кортежем) с необязательным AS перед ним. Пример. Получить меню для бара Joe. SELECT s.beer.name, s.price FROM Sells s WHERE s.bar.name = "Joe's Bar" Отметьте использование двойных кавычек в OQL. Другой способ получить тот же результат, используя объекты класса Bar. SELECT s.beer.name, s.price FROM Bars b, b.beersSold s WHERE b.name = "Joe's Bar" Заметьте, что типичный элемент b в первой коллекции списка FROM используется для определения второй коллекции. Типичное использование. Если x является объектом, можно уточнять выражения доступа к х подобно тому как это сделано в выражениях s, s.beer, s.beer.name. Если x является коллекцией, его можно использовать в списке FROM, как b.beersSold было использовано выше, чобы получить доступ к аттрибутам x. Использование типов результата. По умолчанию: результатом SELECT является набор структур с полями, взятыми из конечных имен в выражениях (путях) доступа списка SELECT. Пример. SELECT s.beer.name, s.price FROM Bars b, b.beersSold s WHERE b.name = "Joe's Bar" Имеет тип результата: Bag( Struct( name: string, price: real ) ) Переименование полей. Поставьте желаемое имя с двоеточием перед путем доступа. Пример. SELECT beer: s.beer.name, s.price FROM Bars b, b.beersSold s WHERE b.name = "Joe's Bar" имеет тип результата: Bag( Struct( beer: string, price: real ) ) Изменение типа коллекции. Используйте SELECT DISTINCT для получения множества структур вместо набора структур. SELECT DISTINCT s.beer.name, s.price FROM Bars b, b.beersSold s WHERE b.name = "Joe's Bar" Используйте ORDER BY для получения списка структур. joeMenu = SELECT s.beer.name, s.price FROM Bars b, b.beersSold s WHERE b.name = "Joe's Bar" ORDER BY s.price ASC ASC = по возрастанию (умолчание); DESC = по убывавнию. Из списка структур можно извлекать отдельную структура также как из массива, например, cheapest = joeMenu[0].name;