Zagadnienia

13. Obiektowe bazy danych – wprowadzenie

13.1. Modele danych

We współczesnych bazach danych są rozpowszechnione dwa podstawowe modele danych:

  • Dominuje relacyjny model danych, w którym organizacja danych opiera się na pojęciu zbioru.

  • Ostatnio dużą popularność zdobywa obiektowy model danych, będący w pewnym sensie mocno rozszerzoną wersją dawnego sieciowego modelu danych. Swoją popularność zawdzięcza dobremu dopasowaniu do obiektowych języków programowania.

13.2. Programowanie obiektowe

Na czym polega w uproszczeniu programowanie obiektowe? Podstawowym pojęciem jest obiekt, traktowany jako kontener zawierający pewien zbiór wartości. Z obiektem związuje się zbiór specyficznych operacji do obserwacji i zmiany stanu obiektu (czyli wartości w nim zawartych).

Musi istnieć możliwość odwołania się do obiektu, aby wykonać którąś jego operację, dlatego obiekty są nazwane (niekoniecznie bezpośrednio).

Obiekty mogą się do siebie odwoływać.

  • obiekty elementarne: odwołują się tylko do swoich klas

  • obiekty złożone: odwołują się także do innych obiektów (bezpośrednio zależą od nich)

Obiekt może jednak przestać istnieć, może się więc zdarzyć niepowodzenie podczas odwoływania się do niego.

13.2.1. Obiekty trwałe w programowaniu

  • Często zachodzi potrzeba dłuższego przechowania niektórych obiektów (np. między sesjami)

  • Takie obiekty nazywamy trwałymi (ang. persistent)

  • Obiekty trwałe można zachować na dysku w repozytorium obiektów

  • Program korzysta z repozytorium za pośrednictwem pamięci buforowej (cache, obiekty ładuje się na żądanie.

  • Jeśli dany obiekt ma być trwały, to wszystkie obiekty do których się odwołuje też muszą być trwałe.

  • Jako nazwy takiego obiektu (niekoniecznie jedynej) używa się PID (persistent object identifier)

  • Repozytoria obiektów trwałych nie są dzielone, jeśli dodamy współbieżny dostęp otrzymamy prawdziwą obiektową bazę danych.

13.3. Relacyjny model danych

Podstawowe składowe:

  • relacje odpowiadające zarówno typom encji (entity), jak i typom związków (relationship);

  • wiersze (krotki) reprezentujące egzemplarze (wystąpienia) encji i związków;

  • kolumny reprezentujące atrybuty;

  • zbiór wartości, które można umieszczać w danej kolumnie, nazywa się jej dziedziną. Relacje są związane tylko wtedy, gdy mają kolumny o wspólnej dziedzinie.

Relacyjny model danych — operacje
  • Operacje na danych opisuje się:

    • algebrą relacji;

    • rachunkiem relacji (specjalizacja rachunku predykatów pierwszego rzędu).

  • W praktyce używa się języka SQL łączącego obie te metody.

Wady modelu relacyjnego
  • Zorientowany na rekordy (dziedzictwo implementacyjne).

  • Konieczność wcześniejszego określenia ,,schematu” bazy danych, dopiero potem można coś do bazy wstawiać.

  • Nie można wstawiać danych nie pasujących do schematu, oficjalnie nie wolno dodawać nowych atrybutów.

  • Za słaby do reprezentacji wiedzy: tylko jedna konstrukcja (tabela), atomowe atrybuty.

Zagnieżdżony relacyjny model danych
  • Dopuszcza atrybuty o wartościach zbiorowych.

  • Wartości zbiorowe reprezentuje się osobnymi, wewnętrznymi tabelami.

13.4. Obiektowy model danych

  • Jedno podstawowe pojęcie do modelowania świata — obiekt.

  • Z obiektem związany jest jego stan i zachowanie.

  • Stan definiuje się wartościami własności (atrybutów) obiektu, mogą one być

    • wartościami elementarnymi (liczby, napisy) lub

    • obiektami, zawierającymi z kolei inne własności.

  • Tak więc obiekty można definiować rekurencyjnie.

  • Na rynku ok. 25 produktów, np. GemStone, ONTOS, ObjectStore, ENCORE.

    • Nie są to jednak jeszcze pełne i uniwersalne bazy danych.

  • Dlatego wiele relacyjnych DBMS uzupełniono o możliwości obiektowe, np. Oracle.

Własności obiektowego modelu danych
  • Zachowanie obiektu opisuje się zbiorem metod — procedur operujących na stanie obiektu.

  • Każdy obiekt jest jednoznacznie identyfikowany systemowym identyfikatorem (OID).

  • Proste definiowanie obiektów złożonych.

  • Obiekty o takich samych własnościach i zachowaniu grupuje się w klasy. Obiekt może być egzemplarzem tylko jednej klasy lub wielu.

  • Klasy łączy się w hierarchie dziedziczenia, w których podklasa dziedziczy własności i metody nadklasy, dokładając swoje specyficzne.

  • Niektóre modele pozwalają na przesłanianie (overriding) — zmianę odziedziczonej własności lub metody.

Różnice w stosunku do modelu relacyjnego
  • Atrybuty mogą być wielowartościowe.

    • Eliminuje to potrzebę używania większości złączeń równościowych.

  • Możliwość definiowania związków odwrotnych.

  • Nie trzeba definiować kluczy, ich rolę pełnią OIDy.

    • Eliminuje to modyfikowanie wartości klucza.

  • Dwa rodzaje równości: identyczność i równość wartości.

  • Złączenia używane gdy warunki w zapytaniu dotyczą porównywania wartości atrybutów. W przypadku ,,kluczy zewnętrznych” bezpośrednia nawigacja z użyciem OID.

  • Przy ładowaniu powiązanych obiektów z bazy danych do pamięci (buforowanie) OIDy zastępuje się wskaźnikami (pointer swizzling), co wielokrotnie przyśpiesza nawigację.

  • Wersjonowanie (długie transakcje) — tylko w niektórych OBD.

13.4.1. Zapytania

Tryby dostępu

Dwa tryby dostępu:

  • Nawigacyjny: mamy OID obiektu i zaczynając od niego przechodzimy po kolejnych referencjach. Zwykle używany w programach.

  • Język zapytań (często podobny do SQL): zapytanie opisuje zbiór obiektów. Deklaratywność.

Przykład

Przykład: Znaleźć wszystkie projekty z budżetem ponad 100000 złp i mające sponsora z Warszawy

  • Relacyjnie

    {p | (f)(a) Projekt(p) AND Firma(f) AND Adres(a)
     AND p.budżet > 100000 AND p.sponsor = f
     AND f.adres = a AND a.miasto = 'Warszawa'}
    

  • Z użyciem wyrażeń ścieżkowych (co pozwala unikać zmiennych ,,roboczych”)

    {p | Projekt(p) AND p.budżet > 100000
      AND p.sponsor.adres.miasto = 'Warszawa'}
    

Wersje obiektów
  • Potrzebne np. w systemach CAD do eksploracji wariantów.

  • Każda wersja ma własny OID, ale zachowujemy związki wyprowadzenia między wersjami, najczęściej tworzące hierarchię.

  • Hierarchia wersji zwykle trzymana w specjalnym obiekcie generycznym.

  • Dwa rodzaje odwołań do wersji

    • specyficzna (albo statyczna): konkretna wersja

    • generyczna (albo dynamiczna): do domyślnej wersji (zwykle ostatniej).

13.4.2. Problemy

  • Brak optymalizacji zapytań. Główny problem to wyrażenia ścieżkowe, trudno dla nich budować indeksy.

  • Brak dobrej formalizacji, ale są już algebry podobne do algebry relacji.

  • Pięć typowych operacji: suma (union, różnica (difference), selekcja (select), generowanie (generate), odwzorowanie (map).

  • Jednak brak powiązania tych operacji z niskopoziomowymi operacjami fizycznymi (takiego jak w algebrze relacji RBD), stąd ich arbitralność.

  • Brak uniwersalnych języków zapytań. Zwykle brak zagnieżdżonych podzapytań, funkcji agregujących, grupowania. Brak automatycznego wsparcia dla obsługi ekstensji klasy — zbioru jej egzemplarzy; użytkownik musi sam zdefiniować kolekcję (collection) i pilnować jej aktualizacji przy dodawaniu i usuwaniu obiektów.

  • Kompozycyjność

    • W modelu relacyjnym wynik zapytania jest (anonimową) relacją.

    • Można go więc obrobić kolejnym zapytaniem, co umożliwia składanie zapytań.

    • W modelu obiektowym jest gorzej, obiekty ze zbioru wynikowego mogą nie należeć do żadnej klasy.

  • Brak wsparcia dla redefiniowania klas (odpowiednik ALTER z SQL), np. dodawania lub usuwania atrybutów.

  • Brak perspektyw (ale może są zbędne).

  • Brak sensownego mechanizmu definiowania uprawnień, nie wiadomo jaka ziarnistość: obiekt, zbiór, klasa, fragment hierarchii?

  • Bardzo ograniczone więzy spójności, konieczność realizacji metodami.

  • Kłopoty z współbieżnością, ręczne operowanie blokadami.

    • Problem długich transakcji (unika się ich w systemach OLTP dla relacyjnych baz danych).

    • Propozycja: wspólna publiczna baza danych + prywatne bazy danych użytkowników.

13.5. Przykłady obiektowych baz danych

13.5.1. O2

Definiowanie klasy wygląda w O2 następująco:

class Instytut
  type tuple : (obszar-badań : string,
                nazwa-instytutu : string,
                adres : Adres,
                grupa-badawcza : set(Zespół))
  public read nazwa-instytutu, write obszar-badań
  method init(string, string, Adres, set(Zespół)) :
    Instytut is public;

Zamiast tuple można użyć list lub set, a nawet zagnieżdżać je w sobie.

Deklaracja metody występująca powyżej podaje jedynie jej sygnaturę.

Kolekcje muszą być tworzone ręcznie, mamy dwie możliwości. Możemy utworzyć kolekcję jako klasę class Instytuty type set(Instytut); albo też jako nazwaną wartość złożoną

name instytuty: set(Instytut);
Programy

Standardowym językiem do pisania programów jest CO2

execute co2 {
  o2 Instytut tmp;
  tmp = new(Instytut);
  instytuty = set(tmp);
}

Zapytania

Zapytania można zadawać używając języka zapytań OQL, stanowiącego rozszerzenie SQL: select x from x in instytuty where x.obszar-badań = 'Bazy danych';

Można w nim używać wyrażeń ścieżkowych: select x from x in instytuty where x.obszar-badań = 'Bazy danych' and x.adres.kraj = 'Włochy';

13.5.2. Orion

make-class 'Instytut'
  superclasses: nil
  attributes: '((obszar-badań: domain string)
                (nazwa-instytutu: domain string)
                (adres: domain Adres)
                (grupa-badawcza:
                  domain (set-of Zespół)))

Uwagi:

  • Można określać wartości domyślne dla atrybutów

  • Wielodziedziczenie

Zapytania w Orionie
  • Brak złączeń, rzutowanie tylko na jeden atrybut lub wszystkie.

  • Powód: dzięki temu wynik zawsze należy do jakiejś klasy.

(Instytut select :I (:I obszar-badań = 'Bazy danych'))
(Instytut select (:I ((:I obszar-badań = 'Bazy danych')
                       and (:I adres kraj = 'Włochy')))

13.5.3. ODMG

Propozycja ODMG
interface Instytut
  (extent Instytuty
   key nazwa)
  {attribute string nazwa;
   attribute string obszar-badań;
   attribute Adres adres;
   relationship Zespół grupa-badawcza
   inverse Zespół::afiliacja};
Zapytania w ODMG

Dozwolone wyrażenia ścieżkowe selectdistinct x from Instytuty x where x.obszar-badań = 'Bazy danych' and x.adres.kraj = 'Włochy';

13.5.4. Implementacja

Rodzaje OID:

  • logiczne: bez związku z adresem w pamięci zewnętrznej

  • fizyczne: na podstawie położenia

Logiczne OID
  • Orion: <id-klasy,id-egzemplarza>. Definicje atrybutów i metod trzymane w obiekcie reprezentującym klasę, więc potrzebny szybki dostęp do niego. Migracja obiektu z klasy do klasy trudnsa, bo zmienia OID.

  • GemStone: informacja o klasie w samym obiekcie, a nie w OID, wymaga wczesnego pobrania obiektu.

Fizyczne OID
  • O2 używa Wiss (Wisconsin Storage Subsystem), obiekt przechowywany jako rekord Wiss, OID jest identyfikatorem rekordem (RID).

    • Przy zmianie strony forward reference.

    • Wymagane tymczasowe OID dla obiektów utworzonych w pamięci, otrzymują trwały OID dopiero przy zatwierdzaniu (commit).

  • Orion: w wersji rozproszonej OID dodatkowo zawierał identyfikator położenia, nie zmieniający się nawet przy migracji.

Zagadnienia otwarte
  • Czy klasy są obiektami? Inaczej mówiąc, czy schemat trzymany jest osobno?

  • Jakie są dozwolone związki między klasami?

  • Czy i jak jest wspierana nawigacja?

  • Jakie są operacje na obiektach?

  • Jak identyfikuje się obiekty?

  • Jak wybiera się obiekty?

  • Jaką rolę pełni klasyfikacja?

  • Czy klasy obiektów mogą ewoluować?

  • Czy w rozproszonym systemie sieć powinna być widoczna?

  • Czy jest specjalna obsługa obiektów aktywnych?

  • Czy ,,świat” obiektów jest zamknięty (closed) czy otwarty?

13.6. Laboratorium: Obiektowe własności PostgreSQL

13.6.1. Dziedziczenie

Dziedziczenie to jedno z podstawowych pojęć obiektowych baz danych. PostgreSQL umożliwia nakładanie dziedziczenia na tabele.

Zobaczmy to na przykładzie. Utworzymy dwie tabele: tabelę miast i tabelę stolic. Ponieważ stolice także są miastami, chcemy uniknąc dublowania informacji. Jeden ze sposobów to

CREATE TABLE Stolice (
  nazwa       text,
  populacja   real,
  temperatura real
  kraj        char(3)
);
CREATE TABLE Inne_miasta (
  nazwa       text,
  populacja   real,
  temperatura real
);
gdzie listę wszystkich miast otrzymamy z:
CREATE VIEW miasta AS
  SELECT nazwa, populacja, temperatura FROM Stolice
    UNION
  SELECT nazwa, populacja, temperatura FROM Inne_miasta;

W PostgreSQL lepszym rozwiązaniem jest:

CREATE TABLE Miasta (
  nazwa       text,
  populacja   real,
  temperatura real
);
CREATE TABLE Stolice (
  kraj char(3)
) INHERITS (Miasta);

Tabela Stolice dziedziczy wszystkie kolumny (nazwa, populacja, temperatura) z macierzystej tabeli Miasta. W tabeli tej występuje jednak dodatkowa kolumna kraj.

Poniższe zapytanie podaje nazwy wszystkich miast (także stolic), w których średnia temperatura roczna jest wyższa niż 10degC:

SELECT nazwa, temperatura
  FROM Miasta
  WHERE temperatura > 500;
natomiast innym zapytaniem możemy otrzymać listę obejmującą jedynie te z nich, które nie są stolicami:
SELECT nazwa, temperatura
  FROM ONLY Miasta
  WHERE temperatura > 500;
Fraza ONLY powoduje, że zapytanie obejmujące jedynie wiersze znajdujące się bezpośrednio w tabeli Miasta, nie zaś w tabelach dziedziczących z niej. Frazy ONLY można używać w poleceniach SELECT, UPDATE i DELETE.

W PostgreSQL tabela może dziedziczyć również z kilku tabel. Struktura dziedziczenia nie może jednak zawierać cykli.

13.6.2. Definiowanie typów

W PostgreSQL można definiować typy w sposób zbliżony do SQL3. Definicja ma postać:

  CREATE TYPE typ AS OBJECT (
  lista atrybutów i metod
  );

Używając jej możemy zdefiniować typ dla punktów:

CREATE TYPE punkt_t AS OBJECT (
  x NUMERIC,
  y NUMERIC
);

Po zdefiniowaniu typu obiektowego używa się podobnie jak typów standardowych, np. możemy teraz zdefiniować typ dla odcinków:

CREATE TYPE odcinek_t AS OBJECT (
  początek punkt_t,
  koniec   punkt_t
);

Możemy teraz utworzyć tabelę zawierającą odcinki wraz z ich identyfikatorami (ID):

CREATE TABLE Odcinki (
  ID      INT,
  odcinek odcinek_t
);

Typy usuwamy poleceniem DROP:

  DROP TYPE odcinek_t;
Przed usunięciem typu trzeba jednak oczywiście usunąć wszystkie tabele oraz inne typy, które używają danego typu a type, więc powyższe polecenie zakończy się niepowodzeniem. Trzeba najpierw usunąć tabelę Odcinki.

Typów zdefiniowanych można również używać bezpośrednio de definiowania tabel (odpowiednik ,,rowtype” z SQL3). W poleceniu CREATE TABLE można zastąpić listę kolumn słowem kluczowym OF i nazwą typu, np.

  CREATE TABLE Odcinki OF odcinek_t;

13.6.2.1. Tworzenie obiektów zdefiniowanego typu

PostgreSQL dla każdego definiowanego typu tworzy automatycznie funkcję konstruktora o tej samej nazwie. Obiekt typu punkt_t tworzy się wywołując funkcję odcinek_t od (podanej w nawiasach) listy wartości atrybutów, np.

  INSERT INTO Odcinki
  VALUES(27, odcinek_t(punkt_t(0.0, 0.0),
                       punkt_t(3.0, 4.0)));

13.6.3. Zapytania ze zdefiniowanymi typami

Dostęp do składowych obiektu uzyskuje się używając notacji kropkowej. Jeśli O jest pewnym obiektem typu T, którego jedną ze składowych (atrybutów lub metod) jest A, to O.A odnosi się do tej składowej w obiekcie O.

Możemy wyszukać współrzędne początków owszystkich odcinków

  SELECT o.odcinek.początek.x, o.odcinek.początek.y
  FROM Odcinki o;
Użycie aliasu jest w takich sytuacjach obowiązkowe. Zapytanie
  SELECT o.odcinek.koniec
  FROM Odcinki o;
wyszuka końce wszystkich odcinków (wypisując je ewentualnie jako wywołania konstruktora.

13.6.4. Odwołania do obiektów (referencje)

Dla typu t wyrażenie REF t oznacza jego typ referencyjny (,,ID obiektu”). Można go używać w definicjach kolumn:

  CREATE TABLE Odcinki (
    początek REF punkt_t,
    koniec   REF punkt_t
  );

Używając takiego typu trzeba pamiętać o pewnych ograniczeniach. Konkretne referencje muszą odnosić się do wierszy tabeli podanego typu. Na przykład dla tabeli

  CREATE TABLE Punkty OF punkt_t;
możemy umieścić w tabeli Odcinki referencje do wierszy z tej tabeli:
  INSERT INTO Odcinki
  SELECT REF(pp), REF(qq)
  FROM Points pp, Points qq
  WHERE pp.x < qq.x;

Referencje nie mogą natomiast odnosić się do obiektów występujących w kolumnach innych tabel, ani nie mogą być tworzone ad hoc (np. umieszczając w poleceniu INSERT klauzulę VALUES(REF(punkt_t(1,2)), REF(punkt_t(3,4)))).

Przechodzenie po referencji zapisuje się używając notacji kropkowej, np. poniższe zapytanie podaje współrzędne x początków i końców wszystkich odcinków w tabeli Odcinki.

  SELECT ll.początek.x, ll.koniec.x
  FROM Lines2 ll;

13.6.5. Tablice

W Postgresie jako typów kolumn można używać typów tablicowych. Wartością atrybutu może być wtedy cała tablica, tak jak dla kolumny b poniżej.

a b
-
- - -
- - -
-
- - -
- - -
-
- - -
- - -

Aby było to możliwe, należy najpierw zdefiniować relację jako typ, używając frazy AS TABLE OF, np.

  CREATE TYPE wielobok_t AS TABLE OF punkt_t;
definiuje typ wielobok_t jako relację, której krotki są typu punkt_t, tzn. mają dwa atrybuty x i y.

Teraz można zdefiniować relację, w której jedna z kolumn będzie reprezentowała wieloboki, tzn. zbiory punktów, np.

  CREATE TABLE Wieloboki (
    nazwa VARCHAR2(20),
    punkty wielobok_t)
  NESTED TABLE punkty STORE AS TabPunkty;

Relacje odpowiadające poszczególnym wielobokom nie są zapisywane bezpośrednio jako wartości atrybutu punkty, lecz trzyma się je w pojedynczej tabeli, której nazwę należy zadeklarować. Do tabeli tej nie można się odwoływać bezpośrednio.

Przy wstawianiu wierszy do relacji Wieloboki używa się konstruktora typu dla zagnieżdżonej relacji (wielobok_t). Wartość zagnieżdżonej relacji podaje się w nim jako rozdzielaną przecinkami listę wartości odpowiedniego typu (punkt_t), najczęściej także używając konstruktora.

  INSERT INTO Wieloboki
  VALUES('kwadrat',
         wielobok_t(punkt_t(0.0, 0.0), punkt_t(0.0, 1.0),
                    punkt_t(1.0, 0.0), punkt_t(1.0, 1.0)));

Wierzchołki tego kwadratu podaje poniższe zapytanie:

  SELECT punkty
  FROM Wieloboki
  WHERE nazwa = 'kwadrat';

Treść automatycznie generowana z plików źródłowych LaTeXa za pomocą oprogramowania wykorzystującego LaTeXML.

Projekt współfinansowany przez Unię Europejską w ramach Europejskiego Funduszu Społecznego.

Projekt współfinansowany przez Ministerstwo Nauki i Szkolnictwa Wyższego i przez Uniwersytet Warszawski.