Bazy danych w systemach obiektowych Problemy zarządzania danymi • Zarządzanie danymi sprowadza się do realizacji szeregu funkcji typowych dla przetwarzania danych, w dużej mierze niezależnych od środowiska implementacyjnego i przyjętej metodyki projektowania. Do najważniejszych należą: - trwałe i niezawodne przechowywanie dużych ilości informacji; - zapewnienie efektywnego dostępu do tych informacji na żądanie, według zadanych kryteriów wyszukiwania; - zapewnienie możliwości modyfikowania przechowywania informacji; - zagwarantowanie logicznej spójności (integralności) informacji, polegające na niedopuszczaniu do realizacji takich operacji, które naruszają definicje danych lub definicje wzajemnych powiązań logicznych pomiędzy poszczególnymi fragmentami danych; - ochrona danych, rozumiana jako zagwarantowanie tego, że na określonych danych będą wykonywane jedynie dopuszczalne operacje i to przez określonych z góry użytkowników (którymi mogą być programy aplikacyjne, ich poszczególne składniki lub użytkownicy przy terminalach). W konkretnym przypadku nie wszystkie z tych funkcji są jednakowo ważne. Bazy danych w projekcie systemu • Konieczność uwzględnienia bazy danych w projekcie (i później w implementacji) systemu może wynikać z kilku przyczyn takich jak: - niektóre obiekty składające się na system muszą mieć cechę trwałości, czyli powinny istnieć nie tylko w trakcie wykonywania programu, ale także po jego zakończeniu. Jest istotne w takich sytuacjach, w których program uruchamiany jest wielokrotnie i korzysta z wyników poprzedniego uruchomienia lub gdy wyniki jednego programu wykorzystywane są przez inne programy. - program może operować na dużych ilościach danych, niekoniecznie o charakterze trwałym. W modelu obiektowym dane takie są zazwyczaj gromadzone w pojemnikach danych w postaci obiektów, które przechowują i udostępniają te dane. Dla dużych składnic danych znaczenia nabiera problem efektywnych metod organizowania danych w struktury w pamięci zewnętrznej i dostępu do takich struktur według uniwersalnych kryteriów. Realizacja takich metod (zwykle bardzo skomplikowanych) sposobami konwencjonalnymi jest trudna i w praktyce nieopłacalna. Jednym z głównych zadań systemów baz danych jest zapewnienie takich metod. - jako narzędzie implementacyjne może zostać wybrane oprogramowanie do rozwoju aplikacji, wchodzące w skład pakietu konkretnego systemu DBMS. Oprogramowanie takie zawiera pewien język programowania, który wymusza na programiście stosowanie określonych konstrukcji bazodanowych w programie aplikacyjnym. Rzutuje to na projekt systemu, gdyż wówczas szereg decyzji projektowych wynika bezpośrednio z zastosowanego języka. Jeśli jako narzędzie implementacyjne zostanie wybrany język ogólnego przeznaczenia, projektant i programista zachowują większy wpływ na środki zastosowane w implementacji programu, jednak kosztem konieczności stosowania wywołań interfejsu programowania aplikacji oferowanego przez wykorzystywany system baz danych. Systemy obiektowych baz danych - cechy Najważniejszą cechą (poza jawnym uwzględnieniem obiektowego modelu danych) jest jednolity język programowania, w którym obiekty trwałe i obiekty programowe (nietrwałe) traktowane są identycznie. W takich językach nie ma potrzeby dokonywania jawnego dostępu do bazy danych (np. przez zanurzenie w programie zapytań formułowanych w specjalnie do tego przeznaczonym języku dostępu do danych, innym niż język programowania aplikacji); system zarządzania automatycznie zrealizuje taki dostęp, jeśli tylko w programie nastąpi odwołanie do obiektu przechowywanego w bazie danych. Cecha ta jest tak istotna, iż czasami systemy obiektowych baz danych nazywa się „językami programowania obiektowych baz danych”, a także „prawdziwymi systemami obiektowymi”. Te prawdziwe systemy zarządzania obiektowymi bazami danych (ObjectOriented Database Management System – OODBMS) zaczęły powstawać w drugiej połowie lat 80. Najważniejsze z nich to: Orion, Itasca, GemStone, ObjectStore, Ontos, O2, Jasmine, Versant, Objectivity/DB, Statice, GBase. Obiektowe bazy danych łączą w sobie obiektowy model danych z własnościami charakterystycznymi dla tradycyjnych („klasycznych”) baz danych. Podstawową jednostką manipulowania danymi w obiektowych bazach danych jest obiekt, rozumiany jako kompletna, hermetyczna i jednoznacznie identyfikowana struktura, obejmująca zarówno dane (atrybuty), jak i zachowanie (metody). Charakterystyczne cechy Charakterystyczne cechy obiektowego modelu danych, odróżniające go od modelu relacyjnego, a uwzględniane w obiektowych bazach danych, to: - tożsamość obiektu (w modelu relacyjnym encje identyfikowalne są jednoznacznym kluczem będącym pojedynczym atrybutem lub złożeniem kilku atrybutów; w modelu obiektowym obiekty mają tożsamość niezależną od ich wartości), - hermetyzacja (zamknięcie danych i operujących na nich procedur we wnętrzu obiektu, niedostępnym z zewnątrz; w modelu relacyjnym dane i procedury są rozdzielone i ogólnie dostępne), - agregacja (możliwość zagnieżdżania obiektów o dowolnej strukturze w innych obiektach; w klasycznym modelu relacyjnym atrybuty są nierozkładalne i nie mają wewnętrznej struktury), - dziedziczenie (zdolność przejmowania właściwości, takich jak atrybuty i zachowanie, od klas zdefiniowanych na wyższym poziomie abstrakcji; w modelu relacyjnym nieobecne), - rozszerzalność (swoboda definiowania nowych typów danych, stosownie do potrzeb aplikacji; w modelu relacyjnym zestaw typów danych jest zdefiniowany z góry i nie można go modyfikować), - wersjowanie (możliwość utrzymywania wielu wersji tego samego obiektu; jest to uogólnienie mechanizmów historycznych, czyli zależnych od czasu, baz danych). Tradycyjne funkcje zarządzania bazami danych (OODBMS) • Trwałość obiektów i zarządzanie pamięcią zewnętrzną, • Interakcyjne języki zapytań (zapytań typy ad hoc), • Przetwarzanie transakcyjne, • Realizacja współbieżnego dostępu do danych, • Kontrola ograniczeń integralnościowych, • Odtwarzanie po awariach, • Ochrona danych i autoryzacja operacji, • Narzędzia wspomagające tworzenie aplikacji. Nowe funkcje obiektowych baz danych • Obsługa transakcji długotrwałych i zagnieżdżonych, • Zupełność obliczeniowa języka manipulowania danymi, w praktyce oznaczająca możliwość dokonywania takich obliczeń, jak w proceduralnych językach programowania (co jest znacznym rozszerzeniem w stosunku do standardowego dla baz relacyjnych języka SQL), • Zarządzanie wersjami obiektów i schematów baz danych, • Zarządzanie wielkimi obiektami o dowolnej strukturze, takimi jak obiekty multimedialne, • Szeroki zakres modyfikacji schematu bazy danych. Przykład • Prosta baza danych ma za zadanie przechowywanie informacji o powiązaniach rodzinnych. Dla każdego ojca interesuje nas: jego identyfikator PESEL, imię i nazwisko oraz zbiór jego dzieci. Dla każdego dziecka chcemy przechowywać imię i datę urodzenia. Definicja schematu takiej bazy danych podana zostanie w trzech wersjach: wersji obiektowej (język OQL systemu O2), w klasycznej wersji relacyjnej (standardowy język SQL) oraz w rozszerzonej wersji relacyjnej (rozszerzona wersja języka SQL). Przykład – model obiektowy class Ojciec type tuple (pesel: string, imię: string, nazwisko: string, dzieci: set(Dziecko)) method podaj_opis_dzieci: set(string) end class Dziecko type tuple (imię: string, data_ur: Date) method wyświetl end; Komentarz • Schemat obiektowej bazy danych zawiera definicje dwóch klas: Ojciec i Dziecko. Każdy obiekt klasy Ojciec ma wymagane atrybuty pesel, imię i nazwisko oraz atrybut dzieci, którego wartością jest zbiór identyfikatorów obiektów będących dziećmi danego ojca. Dla dziecka przechowujemy jedynie atrybuty podające jego imię i datę urodzenia. Atrybut dzieci klasy Ojciec w naturalny i logiczny sposób realizuje wiązanie pomiędzy ojcem a jego wszystkimi dziećmi. Wiązanie to ma charakter jednokierunkowy: przejście od ojca do zbioru jego dzieci jest bezpośrednie – wystarczy odczytać wartość atrybutu dzieci. Znalezienie odpowiedniego obiektu klasy Ojciec dla danego obiektu klasy Dziecko nie jest już tak proste: wymaga przeglądania wartości atrybutu dzieci wszystkich obiektów klasy Ojciec w celu odnalezienia poszukiwanego identyfikatora obiektu klasy Dziecko. Jeśli w konkretnej aplikacji zachodzi częsta potrzeba wykonywania takiej operacji, a liczba obiektów klasy Ojciec jest duża, sensowne może okazać się dodanie do definicji klasy Dziecko dodatkowego atrybutu, który implementuje wiązanie pomiędzy dzieckiem a jego ojcem w kierunku od dziecka do ojca. W ten sposób, za pomocą dwóch atrybutów w dwóch powiązanych ze sobą logicznie klasach zrealizowane zostanie wiązanie dwukierunkowe. Komentarz - cd Zmieniona definicja klasy Dziecko, z dodanym atrybutem realizującym takie dwukierunkowe wiązanie jest następująca: class Dziecko type tuple (imię: data_ur: ojciec: method wyświetl end; string, Date Ojciec) Uwaga: W definicjach klas Ojciec i Dziecko, oprócz atrybutów zawierają również nagłówki dwóch metod (bezparametrowych), umożliwiające uzyskanie i wyświetlenie wybranych informacji o obiektach tych klas. Takie połączenie wartości i operacji w jednym elemencie aplikacji (w tym wypadku – w klasie), charakterystyczne dla obiektowego modelu danych, jest bardzo naturalne i zarazem wygodne dla użytkownika (klienta) klasy. Przykład – model relacyjny CREATE TABELE Ojcowie (pesel imię nazwisko CREATE TABELE Dzieci (pesel imię data_ur pes_ojca CHAR(11), CHAR(16), CHAR(24); CHAR(11), CHAR(8), DATE, CHAR(11)); Komentarz – model relacyjny • Schemat relacyjnej bazy danych zawiera definicje dwóch relacji: Ojcowie i Dzieci. Relacja Ojcowie ma jedynie atrybuty pesel, imię i nazwisko. Atrybut pesel jest kluczem głównym tej relacji, gdyż jednoznacznie identyfikuje ojca (pojedynczą krotkę relacji). W relacji Dzieci, z uwagi na trudności w rozróżnianiu krotek wyłącznie na podstawie atrybutów oznaczających imię i datę urodzenia, również wprowadzono atrybut pesel, który w sposób jednoznaczny identyfikuje dziecko (krotkę relacji) i jest kluczem głównym tej relacji. Można założyć, że dopóki dziecku nie nadano urzędowo numeru PESEL, system automatycznie przypisze mu pewien jednoznaczny numer z zarezerwowanej do tego celu puli numerów (taka praktyka „numerowania” krotek relacji jest bardzo pożyteczna w przypadku, gdy trudno określić klucz główny lub gdy klucz główny składa się z wielu atrybutów). Związek między ojcami a dziećmi jest reprezentowany w relacji Dzieci dodatkowym atrybutem pes_ojca. Atrybut ten pełni rolę klucza obcego, co oznacza, że wartości tego atrybutu muszą być równe wartościom klucza głównego innej relacji (w tym wypadku relacji Ojcowie). Wiązanie między dzieckiem a jego ojcem ma w tym schemacie charakter jednokierunkowy, przy czym jego kierunek nie może zostać w prosty sposób zmieniony (tak jak to miało miejsce w obiektowej wersji schematu tej bazy danych). Istotnie, wymóg zachowania pierwszej postaci normalnej nie pozwala na dodanie do schematu relacji Ojcowie atrybutu wielowartościowego reprezentującego zbiór dzieci danego ojca (w tym wypadku byłby to zbiór wartości klucza głównego relacji Dzieci). Dodanie wielu atrybutów prostych, po jednym na każde dziecko, jest również niemożliwe, gdyż liczba atrybutów w każdej krotce relacji musi być z góry ustalona i jednakowa. Komentarz – model relacyjny • Z uwagi na jednokierunkowy charakter wiązania dziecko-ojciec, jedynie operacja znalezienia ojca dla danego dziecka może być realizowana bezpośrednio: wystarczy w tym celu odczytać wartość atrybutu pes_ojca dla danej krotki relacji Dzieci. Symetryczna do niej operacja znalezienia zbioru dzieci dla konkretnego ojca (czyli dla danej krotki relacji Ojcowie) wymaga przejrzenia wszystkich krotek relacji Dzieci w poszukiwaniu tych wartości atrybutu pes_ojca, które są równe wartości klucza głównego danej krotki relacji Ojcowie. Jeśli taka operacja wykonywana jest często, a relacja Dzieci zawiera dużą liczbę krotek, może okazać się konieczne zastosowanie pomocniczych struktur zwanych indeksami, które pozwalają na efektywną implementcję poszukiwania w danej relacji krotek o zadanej wartości wybranego atrybutu. Standard języka SQL obejmuje instrukcje pozwalające projektantom baz danych tworzyć indeksy, stosownie do potrzeb danej aplikacji. W omawianym przykładzie, taki indeks posłużyłby do realizacji (wielowartościowego) odwzorowania każdej z wartości klucza głównego relacji Ojcowie w zbiór odpowiadających jej wartości klucza głównego relacji Dzieci. Indeks taki posłużyłby do implementacji drugiego kierunku związku pomiędzy ojcami a dziećmi. Komentarz – model relacyjny • Standard języka SQL nie daje środków wspomagających zachowanie podstawowych własności kluczy głównych i kluczy obcych. Od wartości klucza głównego relacji wymaga się, by były jednoznaczne i niepuste. Od wartości klucza obcego w konkretnej krotce wymaga się, by była albo pusta albo równa wartości klucza głównego w pewnej krotce relacji z nią powiązanej. Aby wspomóc użytkownika w zachowaniu tego typu ograniczeń integralnościowych, producenci wprowadzają różne rozszerzenia do części języka SQL zwanej językiem definiowania danych (Data Definition Language). Poniżej zamieszczono poprzedni schemat relacyjnej bazy danych z uwzględnieniem możliwości, jakie daje język SQL z rozszerzeniami wprowadzonymi przez firmę Informix. Atrybuty pełniące rolę kluczy głównych opatrzono frazą PRIMARY KEY. Atrybut pełniący rolę klucza obcego opatrzono frazą REFERENCES –daje to możliwość kontroli powiązań pomiędzy krotkami obu relacji. CREATE TABELE Ojcowie (pesel CHAR(11) PRIMARY KEY, imię CHAR(16), nazwisko CHAR(24); CREATE TABELE Dzieci (pesel CHAR(11) PRIMARY KEY, imię CHAR(8), data_ur DATE, pes_ojca CHAR(11) REFERENCES); Komentarz – model relacyjny • W schematach relacji Ojcowie i Dzieci nie ma możliwości zawarcia definicji operacji, które mogą być wykonywane na poszczególnych krotkach. Schemat relacyjnej bazy danych zawiera jedynie opis danych, bez opisu zachowania się (funkcji) elementów przechowywanych w bazie. Jednak współczesne systemy RDBMS dopuszczają możliwość przechowywania w bazie danych nie tylko danych statystycznych, ale także procedur (stored procedure), choć wiązanie ich z danymi musi być nadal realizowane jawnie przez program aplikacyjny. Komentarz – rozszerzony model relacyjny TYPE Dziecko = ROW (imię data_ur CHAR(16), DATE); CREATE TABELE Ojcowie (pesel imię nazwisko dzieci CHAR(11), CHAR(16), CHAR(24), SET(Dziecko)); Komentarz – rozszerzony model relacyjny • • Schemat relacyjnej bazy danych ograniczono tu do jednej relacji Ojcowie, przy czym w porównaniu z odpowiadającą jej relacją z klasycznego modelu relacyjnego dodany został atrybut wielowartościowy dzieci, reprezentujący zbiór dzieci danego ojca. Zastosowano tu charakterystyczną dla rozszerzonych systemów relacyjnych możliwość zagnieżdżania jednych krotek w innych (a więc odejścia od pierwszej postaci normalnej). W tym celu zdefiniowano typ wierszowy Dziecko, definiujący strukturę krotek zawierających dane dotyczące dzieci. Odpowiednie krotki z tymi danymi, będące konkretnymi wystąpieniami typu Dziecko, zagnieżdżane są w stosownej krotce relacji Ojcowie. Rozwiązanie to jest podobne do rozwiązania zastosowanego w modelu obiektowym; jednak występują tu pewne zasadnicze różnice. W modelu obiektowym zdefiniowano oddzielną klasę Dziecko, co powoduje, że obiekty tej klasy mają swoją tożsamość (atrybut dzieci klasy Ojciec zawiera zbiór identyfikatorów obiektów będących dziećmi danego ojca, a nie zbiór wartości tych obiektów). Natomiast w rozszerzonym modelu relacyjnym atrybut dzieci relacji Ojcowie zawiera zbiór wartości atrybutów opisujących dzieci danego ojca. Związek między dziećmi a ojcami został tu zrealizowany w postaci jawnego zagnieżdżenia wartości jednej encji w innej encji, przy czym encja zagnieżdżona utraciła przy tym swoją tożsamość (nie ma swojego klucza głównego, a więc nie może być odróżniona od innych). Rozwiązanie to można zmodyfikować na kilka sposobów. Jedną z możliwości jest dodanie do definicji typu Dziecko atrybutu kluczowego pesel (analogicznie do modelu relacyjnego, bez zmiany schematu relacji Ojcowie. Wówczas pozostaje implementacja związku przez zagnieżdżenie, jednak encja zagnieżdżona nie traci swojej tożsamości (rozumianej jako możliwość odróżnienia jej od innej encji tego samego typu). Drugą możliwością jest zdefiniowanie oddzielnej relacji Dzieci, zawierającej dane opisujące dzieci, podobnie jak to uczyniono w modelu relacyjnym. Taki schemat przejmuje z modelu obiektowego możliwość stosowania atrybutów wielowartościowych (co np. umożliwia naturalną implementację związków dwukierunkowych bez względu na krotność), a z modelu relacyjnego realizację związków pomiędzy relacjami za pomocą kluczy obcych. Komentarz – rozszerzony model relacyjny CREATE TABELE Ojcowie (pesel CHAR(11), imię CHAR(16), nazwisko CHAR(24), dzieci SET(CHAR(11)); CREATE TABELE Dzieci (pesel CHAR(11), imię CHAR(8), data_ur DATE, pes_ojca CHAR(11)); W rozszerzonych systemach relacyjnych wprowadza się pewne możliwości bezpośredniego wiązania danych z operacjami. Decyzje projektowe • Problemy jakie występują, gdy model klas projektowanego systemu chce się przełożyć na projekt bazy danych dotyczą zwykle decyzji projektowych lub czysto implementacyjnych. Podjąć je należy w momencie przekładania modelu na projekt, ukierunkowany na określone środowisko implementacyjne. Charakter tych decyzji zależy w dużej mierze od modelu danych przyjętego w systemie baz danych w którym projektowana baza danych będzie implementowana (decyzje odnośnie sposobu implementacji związków między obiektami czy kwestie zagnieżdżania danych). Implementacja związków • • • Zależności pomiędzy klasami (zw. też związkami), występujące w modelu klas, nie mają z góry określonej kierunkowości (z punktu widzenia logiki modelu są dwukierunkowe). W projekcie bazy danych trzeba zdecydować się na ustalenie kierunku związku, a ściślej – na sprecyzowanie kierunku połączenia (zw. wiązaniem), istniejącego pomiędzy dwoma obiektami powiązanych ze sobą klas. Z uwagi na możliwość definiowania w strukturze obiektu atrybutów wielowartościowych, dowolny związek binarny można implementować w postaci związku jednokierunkowego (w dowolnym kierunku) albo w postaci związku dwukierunkowego. Niezależnie od krotności związku nie musimy do jego implementacji używać żadnych oddzielnych struktur – wystarczy dodanie odpowiedniego atrybutu do definicji klas. Tak uczyniono w schemacie obiektowym (atrybut dzieci klasy Ojciec i atrybut ojciec zmodyfikowanej klasy Dziecko) oraz w rozszerzonym schemacie relacyjnym w jego drugiej wersji (atrybut dzieci relacji Ojcowie i atrybut pes_ojca relacji Dzieci). Implementacja związku w postaci dwukierunkowej nakłada dodatkowe ograniczenia integralnościowe związane z aktualizacją wiązań. Ograniczenia te polegają na tym, że: - aktualizacja atrybutu reprezentującego wiązanie w jednym obiekcie musi pociągać za sobą aktualizację odpowiedniego atrybutu w obiekcie z nim powiązanym, - aktualizacja obu atrybutów musi być wykonana w ramach jednej transakcji (musi być operacją atomową). Uwaga: jeśli którykolwiek z powyższych warunków nie będzie zachowany, wiązanie utraci charakter dwukierunkowości. Implementacja związków - cd • W modelu obiektowej bazy danych może być również uzasadnione reprezentowanie związku przez specjalnie w tym celu wprowadzoną klasę. Metodę tę należy stosować wówczas, gdy stopień związku jest wyższy niż dwa (tzn. liczba związanych ze sobą klas jest większa niż dwa), a także wtedy, gdy z semantyki modelu wynika, że ze związkiem skojarzone są konkretne atrybuty, które nie są atrybutami klas pozostających w tym związku. Występuje tu pełna zgodność z metodą postępowania stosowaną podczas analizy obiektowej (związki posiadające własne atrybuty stają się klasami). Przykład • Mamy klasę Mężczyzna i Kobieta, powiązane ze sobą związkiem Małżeństwo. Pomiędzy dwoma obiektami z tych klas wiązanie istnieje wtedy, gdy dwie osoby reprezentowane przez te obiekty pozostają ze sobą w związku małżeńskim. Najlogiczniejszym miejscem dla atrybutu oznaczającego datę zawarcie związku małżeńskiego jest opis samego związku. Z każdym związkiem wiążemy wówczas jedną wartość (datę). W momencie tworzenia wiązania (czyli w chwili zawarcia związku małżeńskiego) tworzymy nowy obiekt reprezentujący to wiązanie i nadajemy wartość jego atrybutom (dacie). W zależności od tego, czy decydujemy się na wiązanie jedno- czy dwukierunkowe, dodatkowo może zajść potrzeba zapisania w odpowiednich obiektach klasy Mężczyzna i klasy Kobieta wskazań na nowo utworzony obiekt klasy Małżeństwo. Gdybyśmy atrybut oznaczający datę umieścili w obu powiązanych ze sobą klasach, pojawiłyby się problemy wielu kopii tej samej informacji oraz typowe problemy integralnościowe przy aktualizacjach. Umieszczenie tego atrybutu w tylko jednej z tych klas powodowałoby niejednakowe traktowanie tych klas, a tym samym zachwianie logicznej spójności modelu. Przykład – fragment schematu • • Poniżej przedstawiono fragment schematu obiektowej bazy danych, w którym związek Małżeństwo zaimplementowano jako klasę. W schemacie tym zastosowano wiązania jednokierunkowe pomiędzy obiektami klasy Małżeństwo a odpowiadającymi im obiektami klasy Mężczyzna i klasy Kobieta. Uwaga: Dla prostoty pominięto szereg atrybutów i metod, które mogłyby być istotne w danej bazie danych, a także możliwość zastosowania uogólnienia, które mogłoby tu polegać na wydzieleniu wspólnych atrybutów klas Mężczyzna i Kobieta (np. nazwisko i imię, adres) do wspólnej nadklasy. class Mężczyzna type tuple end; class Kobieta type tuple (nazwisko:string, imię: string, …) (nazwisko:string, imię: string, …) end; class Małżeństwo type tuple end; (data: mąż: żona: Date, Mężczyzna, Kobieta) Osadzanie obiektów • • Osadzanie obiektów w innych obiektach może być uzasadnione ze względu na efektywność. Dostęp do wartości obiektu osadzonego z poziomu obiektu złożonego jest bezpośredni – polega jedynie na odczytaniu wartości atrybutu (lub atrybutów). W wypadku przechowywania obiektów w bazach danych należy się spodziewać, że wartości atrybutów jednego obiektu położone są fizycznie blisko siebie, prawdopodobnie na jednej stronie dyskowej lub – jeśli obiekt ma duży rozmiar – na niewielu sąsiadujących ze sobą stronach. W związku z tym wartości te pobierane są do pamięci operacyjnej bardzo efektywnie, zwykle w ramach jednej transmisji dyskowej. Jeśli związany obiekt nie jest osadzony, a jedynie zagniżdżony (tzn. w obiekcie złożonym istnieje jawne odwołanie do obiektu składowego, poprzez jego identyfikator), wówczas dostęp do obiektu składowego wymaga z reguły przynajmniej jeszcze jednego, dodatkowego dostępu do dysku. W projekcie schematu obiektowej bazy danych możliwy jest również proces odwrotny do osadzania, polegający na wydzieleniu pewnej części wartości obiektu i potraktowaniu jej jako samodzielnego, „pełnoprawnego” obiektu. Może to być celowe, gdy zdarza się, że liczne obiekty tej samej klasy mają tę samą wartość pewnych atrybutów. Przykład – fragment bazy o osobach • Baza danych zawiera m.in. Informacje adresowe. Definicja klasy Osoba w takiej bazie może wyglądać następująco: class Osoba type tuple (nazwisko: string, imię: string, adr_miasto: string, adr_kod: string, adr_ulica: string, adr_nr_ul: integer) end; Komentarz • Jeśli szereg osób mieszka pod tym samym adresem, wartości wszystkich atrybutów o nazwach rozpoczynających się od adr_ będą się powtarzać. Rozszerzymy schemat naszej bazy danych o klasę Adres, której wystąpieniami są adresy, pod którymi mogą mieszkać osoby oraz zmodyfikujemy odpowiednio definicję klasy Osoba. W efekcie uzyskamy następujący schemat: class Osoba type tuple end; class Adres type tuple end; (nazwisko: imię: adres: string, string, Adres) (miasto: kod: ulica: nr_ul: string, string, string, integer) Obiekty z osadzonymi identycznymi adresami – graf kompozycji id1 nazwisko imię Nowak id2 adres nazwisko Kłos Jan miasto kod ulica nr_ul Olsztyn 10-900 Wąska 12 imię adres Jerzy miasto kod ulica nr_ul Olsztyn 10-900 Wąska 12 Różnice w zawartościach obu baz zobrazowano w grafach kompozycji dwóch obiektów O1 i O2 klasy Osoba, o identyfikatorach odpowiednio O1 i O2 mieszkających pod tym samym adresem. Na rysunku przedstawiono wariant w którym adres jest wartością. Obiekty z osadzonymi identycznymi adresami – graf kompozycji Obiekty z identycznym adresem, reprezentowanym jako obiekt id1 nazwisko imię Nowak id2 adres Jan adres id3 imię nazwisko Jerzy Kłos miasto kod ulica nr_ul Olsztyn 10-900 Wąska 12 Zaprezentowane tu wydzielenie części wartości obiektu jako oddzielnego obiektu w praktyce może mieć głębsze znaczenie niż tylko ukierunkowane implementacyjnie zmniejszenie zajętości pamięci. Jeśli wydzielona struktura jest złożona, może okazać się pożyteczne przypisanie do niej stosownych metod, np. służących do jej prezentacji lub zmiany wartości. Takie postępowanie sprzyja polepszeniu modularności i Trwałość obiektów • Jednym z najważniejszych wymogów stawianych bazom danych jest cecha trwałości (persistence), polegająca na tym, że dane utworzone przez program mogą istnieć dłużej niż okres aktywności tego programu. To jak długo mogą istnieć dane, zależy wyłącznie od ich właściciela (zazwyczaj jest nim użytkownik, który je utworzył). • Podejście obiektowe w zastosowaniu do baz danych wpływa w istotny sposób na interpretację cechy trwałości elementów aplikacji, w tym wypadku – trwałości obiektów. Jednym z istotnych postulatów obiektowego modelu danych jest jednorodność, przejawiająca się w różnych aspektach, także w aspekcie jednakowego traktowania obiektów trwałych (przechowywanych w bazie danych) i obiektów nietrwałych (programowych). Takie jednakowe traktowanie przejawia się w tym, że operacje wykonywane na obiektach nie zależą od cech trwałości obiektów. Jest to dodatkowy i bardzo pożyteczny w praktyce aspekt niezależności programu od danych (data independence). Trwałość obiektów - wymagania • Istotne wymagania odnośnie cechy trwałości obiektów to: - ortogonalność – trwałość obiektu nie zależy od klasy, do której należy dany obiekt; innymi słowy, każdy obiekt, niezależnie od jego klasy, może być uczyniony obiektem trwałym lub obiektem nietrwałym; - przezroczystość – obiekty trwałe i obiekty nietrwałe mogą być traktowane w programie w ten sam sposób; innymi słowy, z punktu widzenia programu aplikacyjnego, nie ma różnicy w dostępie do danych rezydujących w pamięci operacyjnej i do danych przechowywanych w pamięci dyskowej. • Wszystkie „prawdziwe” systemy obiektowe spełniają postulat przezroczystości (transparency). Spełnienie postulatu ortogonalności zależy natomiast od konkretnego systemu. Przykład • Schemat osobowej bazy danych rozszerzono o definicję klasy kontenerowej o nazwie ZbiórOsób. W tym schemacie zdefiniowano również obiekt nazwany TenZbiórOsób, będący wystąpieniem klasy ZbiórOsób. Ponieważ obiekt TenZbiórOsób po utworzeniu go w klasie ZbiórOsób i przypisaniu mu nazwy staje się obiektem trwałym, każdy obiekt klasy Osoba umieszczony w zbiorze będącym atrybutem zbiór obiektu TenZbiórOsób staje się też obiektem trwałym. Również każdy obiekt klasy Adres, do którego odwołuje się dowolny obiekt trwały klasy Osoba, jest obiektem trwałym. Pozostałe obiekty klas Osoba i Adres są nietrwałe. class ZbiórOsób type tuple (zbiór: set(Osoba), licznik: integer, method dodaj_osobę (osoba: Osoba), … end; class Osoba type tuple (nazwisko:string, imię: string, adres: Adres) end; class Adres type tuple (miasto: string, kod: string, ulica: string, nr_ul: integer) end; Name TenZbiórOsób: ZbiórOsób; Baza danych - Armatorzy • System komputerowy (Armatorzy) ma zarządzać danymi o firmach eksploatujących statki. Każda firma może mieć kilka siedzib (filii), przy czym siedziby jednej z nich są uporządkowane według pewnego kryterium ważności (nie jest one dla nas ważne). Każda siedziba ma swoją nazwę. W jednej siedzibie może mieścić się wiele firm. Dla każdej firmy chcemy znać: nazwę, rok założenia, liczbę zatrudnionych, a także wiedzieć, gdzie ma swoje siedziby i jakie statki eksploatuje. Każdy statek eksploatowany przez firmę ma nazwę i wyporność. Wśród statków wyróżniamy promy i żaglowce. Każdy prom, oprócz atrybutów właściwych dla każdego statku, ma określoną trasę kursowania. Dla każdego żaglowca, oprócz atrybutów właściwych dla każdego statku, interesuje nas dodatkowo powierzchnia żagli i typ żaglowca. Każdy statek eksploatowany jest przez jednego armatora (firmę). Każdy został zwodowany w określonej stoczni. Dla każdej stoczni interesuje nas: lokalizacja, określona przez miasto i państwo, pewien parametr ekonomiczny (średni zysk ze sprzedaży jednego statku, w procentach) oraz to jakie statki (spośród tych, które są eksploatowane przez firmy) w niej zwodowano. Diagram klas dla systemu Armatorzy class Class Model Firma Statek eksploatuje - Stocznia zwodowany nazwa: rok-założenia: zatrudnienie: 1.. - nazwa: wyporność: - miasto: państwo: zysk-jednostkowy: ma siedzibę {kolejność} Żaglow iec Prom Siedziba - nazwa: trasa: - powierzchnia_żagli: typ: Na diagramie zaznaczono jedynie te atrybuty, które zostały jawnie wymienione w sformułowaniu problemu. W trakcie głębszej analizy pojawiłaby się konieczność zawarcia w modelu innych atrybutów, a także operacji, które dla uproszczenia zostały pominięte. Schemat obiektowej bazy danych Armatorzy class Class Model Firma Statek nazwa: rok-założenia: statki: zatrudnienie: siedziby: Stocznia nazwa: producent: wyporność : Prom trasa: miasto: państwo: zysk-jednostkowy: Żaglow iec powierzchnia_żagli: typ: Na schemat każdej obiektowej bazy danych składają się dwie hierarchie: hierarchia dziedziczenia, reprezentująca związek nadklasa- podklasa oraz hierarchia kompozycji, reprezentująca związek atrybut-dziedzina. W ogólności hierarchię dziedziczenia w obiektowej bazie danych można przedstawić w postaci skierowanego grafu acyklicznego, natomiast hierarchię kompozycji w postaci dowolnego grafu skierowanego. Węzłami w obu hierarchiach są te same klasy. Na diagramie strzałki ciągłe oznaczają związek nadklasa-podklasa (są więc krawędziami w hierarchii dziedziczenia), natomiast strzałki przerywane oznaczają związek atrybutu-dziedzina (są krawędziami w hierarchii kompozycji). Znak * obok nazwy atrybutu oznacza, że jest to atrybut wielowartościowy, czyli taki, którego wartością jest zbiór (jak atrybut statki klasy Firma) lub lista (jak atrybut siedziby klasy Firma). Przykład - Armatorzy • Niżej zapisano ten schemat w języku definiowania schematów systemu O2. zgodnie z konwencją przyjęto w tym języku, dziedziny atrybutów pisane małą literą są dziedzinami prymitywnymi (czyli takimi, których elementy są wartościami atomowymi), natomiast dziedziny pisaną dużą literą oznaczają klasy (elementami tych dziedzin są identyfikatory obiektów odpowiednich klas). Klasy Prom i Żaglowiec mają również atrybuty producent i wyporność, choć nie występują one jawnie w opisach tych klas. Atrybuty te są dziedziczone przez te klasy z klasy Statek, co zapewnia fraza inherit występująca w definicjach klas Prom i Żaglowiec. Aby zapewnić trwałość obiektów z bazy Armatorzy, do schematu tej bazy dodano nazwę ZbiórFirm, która będzie pełniła rolę korzenia trwałości i w trakcie tworzenia bazy danych zostanie przypisana do pustego zbioru obiektów klasa Firma. Przykład - Armatorzy class Firma type tuple end; class Statek type tuple end; class Stocznia type tuple (nazwa: rok_założenia: statki: zatrudnia: siedziby: string, integer, set(Statek), integer, list(string)) (nazwa: producent: wyporność: string, Stocznia, integer) (miasto: string, państwo: string, zysk_jednostkowy: integer) end; class Prom inherit Statek type tuple (trasa: string) end; class Żaglowiec inherit Statek type tuple (powierzchnia_żagli: typ: string) end; name ZbiórFirm: set(Firma); integer,