I.
Wprowadzenie do  języków zapytań (1)
II.
Wprowadzenie do  języków zapytań (2)
III.
Pojęcia obiektowości w bazach danych (1)
IV.
Pojęcia obiektowości w bazach danych (2)
V.
Podstawy semantyczne języków zapytań
VI.
Modele składu obiektów
VII.
Stos środowisk, rezultaty zapytań, funkcja nested
VIII.
Język SBQL (Stack-Based Query Language) (1)
IX.
X.
Dalsze własności SBQL
  Wstęp
  1. Rozszerzenie SBQL w modelu M1
  2. Rozszerzenie SBQL w modelu M2
  Podsumowanie
  Zadania
XI.
Operatory order by i group by
XII.
Przetwarzanie struktur nieregularnych
XIII.
Rozszerzenie języków zapytań o konstrukcje imperatywne
XIV.
Procedury, procedury funkcyjne, metody, reguły zakresu
XV.
Parametry procedur i metod, procedury rekurencyjne, optymalizacja poprzez modyfikację zapytań

 

2. Rozszerzenie SBQL w modelu M2

Jak dotąd, tylko podejście stosowe umożliwia definicję języka zapytań dla modelu obiektowości zakładającego dynamiczne role obiektów (określanego przez nas jako model M2). Pozostałe podejścia do języków zapytań, jak dotąd, nie rozważają (nie zauważyły) takiej koncepcji. Można jednak z góry twierdzić, że opanowanie pełnej semantyki tego modelu na gruncie algebry, logiki lub jakiegoś wyspecjalizowanego rachunku będzie nieuchronnie prowadzić do ograniczeń koncepcji i zasadniczych trudności pojęciowo-formalnych.

Zmiana w stosunku do modeli M0 i M1 polega na sposobie zapełniania bazowych sekcji stosu. Mianowicie, w tym przypadku sekcja bazowa musi zawierać bindery do wszystkich ról obiektów. Jest to zgodne z interpretacją modelu M2 przedstawionego na Rys.30 i Rys.31, gdzie identyfikatory startowe (zbiór) R obejmują wszystkie aktualne role wszystkich obiektów. Zwrócimy uwagę, że w naszym modelu M2 obiekt występuje wyłącznie jako konglomerat ról, z jedną wyróżnioną rolą główną. Dla tego przykładu dolne sekcje stosu ENVS są przedstawione na Rys.62.

62
Rys.62. Obraz dolnych sekcji stosu ENVS w modelu M2

Dla przykładu modelu M2 przestawionego na Rys.31 pokażemy, w jaki sposób zarządza się stosem środowiskowym dla operatora niealgebraicznego where, Rys.63. Podobnie stos będzie się zachowywać dla innych operatorów niealgebraicznych. Rozważmy zapytanie:

P10.7.

Prac where ... n ...

gdzie n jest dowolną nazwą występującą wewnątrz podzapytania po where; n nie jest objęta innym operatorem niealgebraicznym. Zakładamy, że aktualnie operator where przetwarza rolę Prac posiadającą identyfikator i16.

Przykładowo, dla zapytania

P10.8.

Prac where Wiek > 35

nazwa Wiek będzie związana w czwartej sekcji stosu, licząc od góry.  Uogólnimy sytuację przedstawioną na Rys.63. Załóżmy, że rola R1 dziedziczy dynamicznie z roli R2 która dziedziczy dynamicznie z roli R3 itd. Niech Ri (i=1,2,...) będzie członkiem klasy C1Ri która dziedziczy statycznie z klasy C2Ri, która dziedziczy statycznie z klasy C3Ri itd. Oznaczmy odpowiednie identyfikatory przez iR1, iR2, iR3, ..., iC1R1, iC2R1, iC3R1, ..., iC1R2, ... itd. Oczywiście, klasy CjRi (j = 1,2,...) nie muszą być unikalne; mogą tworzyć pewien graf dziedziczenia. Sytuacja ta jest przedstawiona na Rys.65.

Rozważmy zapytanie q1 q q2, gdzie q jest pewnym operatorem niealgebraicznym, zaś q1q2 są podzapytaniami. Załóżmy, że q1 zwraca identyfikator roli R1. Wówczas q wkłada na wierzchołek stosu ENVS sekwencję sekcji przedstawioną na Rys.64. Może się zdarzyć, że pewne sekcje klas wkładanych na stos będą się powtarzać. Poza koncepcyjną redundancją (którą można łatwo wyeliminować w implementacji) w niczym to oczywiście nie przeszkadza, ponieważ istotna będzie tylko ta sekcja, która jest najbliższa wierzchołka stosu. Duplikaty tej sekcji znajdujące się poniżej tej sekcji nie będą uczestniczyć w wiązaniu nazw.

63
Rys.63. Stan stosu ENVS dla modelu z dynamicznymi rolami

64
Rys.64. M2: sekcje wkładane na stos ENVS przez operator niealgebraiczny

Po ewaluacji zapytania q2 wszystkie te sekcje będą zdjęte ze stosu. Reguły wiązania nazw są takie same, jak w przypadku modelu M0. Nie występują tu anomalie przy wiązaniu nazw, które były omawiane przy okazji modelu M1.

65
Rys.65. Ogólny obraz składu z rolami i klasami


2.1. Operatory rzutowania w modelu M2

Model M2 implikuje operator algebraiczny znany z innych języków pod nazwą "rzutowanie" (casting). W operatorze tym chodzi o to, aby mieć możliwość przełączenia się z danej roli obiektu do innej roli tego samego obiektu. Przykładowym zapytaniem, które może wymagać takiego operatora, może być: "Podaj wszystkich pracowników, którzy są jednocześnie studentami". W odróżnieniu od operatora rzutowania znanego z języka C++ (który jest głównie używany podczas kompilacji) nasz operator będzie operatorem czasu wykonania dokonującym konwersji zbioru identyfikatorów ról na inny zbiór identyfikatorów ról. Syntaktycznie operator ten będzie zapisywany w postaci:

(nazwa) zapytanie

gdzie nazwa jest nazwą roli, zapytanie zwraca bag identyfikatorów ról.

Semantycznie operator ten dla danego identyfikatora roli zwróconego przez zapytanie wyszukuje w ramach tego samego obiektu role posiadające nazwę nazwa. W ogólnym przypadku taka rola może nie istnieć - wówczas operator zwraca pusty bag. W przeciwnym przypadku operator zwraca odpowiednie identyfikator roli (lub identyfikatory ról). Końcowy wynik jest sumą mnogościową wyników dla wszystkich identyfikatorów zwróconych przez zapytanie.

P10.9.

Podaj pracowników, którzy są jednocześnie studentami:

(Prac) Student

Ewaluacja zapytania Student zwróci wszystkie identyfikatory wszystkich podról ról Osoba nazwanych Student. Operator rzutowania (Prac) zmieni niektóre
z nich na identyfikatory podról Prac (jeżeli obiekt ma jednocześnie role StudentPrac), zaś inne na wartość pustą.

P10.10.

Załóżmy, że role Student mają atrybut Stypendium. Dla każdej osoby należy zwrócić Nazwisko oraz dochody, które wynoszą 0, jeżeli dana osoba nie jest ani pracownikiem, ani studentem, Zar, jeżeli osoba jest pracownikiem, Stypendium, jeżeli osoba jest studentem lub Zar+Stypendium, jeżeli osoba jest jednocześnie pracownikiem i studentem.

(Osoba as p). (p.Nazwisko, sum( bag( 0, ((Student)p).Stypendium, ((Prac)p).Zar))

Pomocnicza nazwa p obiega wszystkie role Osoba. Po pierwszej kropce następuje wyliczenie wyniku dla pojedynczej wartości p. Na wynik ten składa się Nazwisko danej osoby oraz suma dochodów. Dla wyliczenie tej sumy tworzy się bag składając się z jednego, dwóch lub trzech elementów. Jeżeli p posiada rolę Student, to p jest rzutowane na tę rolę, a następnie z tej roli pobiera się Stypendium; podobnie dla roli Prac i atrybutu Zar.

Zwrócimy uwagę, że podane zapytanie w SBQL automatycznie uwzględni fakt, że dana osoba jest kilkakrotnie pracownikiem i/lub kilkakrotnie studentem. W takim przypadku operator rzutowania dla danego identyfikatora roli Osoba zwróci dwa lub więcej identyfikatorów ról Student lub Prac, ale oczywiście, w podanym zapytaniu nic to nie przeszkadza.

Innym udogodnieniem może być operator algebraiczny, który umożliwiłby przetestowanie, czy dana rola posiada podrolę o określonej nazwie. Niech taki operator ma postać

Rola has role nazwaRoli

gdzie zarówno nazwaRoli, jak i Rola są zapytaniami; nazwaRoli zwraca string będący nazwą testowanej roli, Rola zwraca identyfikator roli, np.:

P10.11.

Podaj, ile osób jest jednocześnie pracownikiem i studentem:

count( (Osoba as p) where (p has role"Prac") and (p has role"Student"))

Podane wyżej możliwości ułatwiają generyczne programowanie, ale nie rozwiązują kompleksowo tego problemu. Generalne rozwiązanie wymagałoby stworzenia metamodelu oraz repozytorium metadanych dla przechowywania wszelkich metainformacji związanych z definicjami klas, typów, interfejsów itd. w modelu M2 oraz powiązanie tego repozytorium z obiektami przechowywanymi w składzie. Propozycja takiego repozytorium jest zawarta w [Habe02a, Habe02b, Habe03b, Habe03a, Jodl03b, Subi03]. Takie repozytorium mogłoby być przeszukiwane za pomocą standardowego SBQL. Pełne możliwości programowania generycznego wymagałyby dodatkowo stworzenia mechanizmu refleksji [Roan02, Habe02a].


2.2. Odmienność i zalety modelu z rolami

Niżej podsumujemy własności modelu z dynamicznymi rolami starając się podkreślić jego zalety i odmienność.

  • Wielokrotne dziedziczenie: Ponieważ role są hermetyzowane, nie może wystąpić konflikt nazw nawet wtedy, gdy różne role (czyli specjalizacje obiektu) posiadają własności o tych samych nazwach.

  • Powtarzalne dziedziczenie: Jest normalne, że obiekt może mieć dwie lub więcej ról o tej samej nazwie. Np. Kowalski może być dwa razy studentem, w różnych szkołach. Ten przypadek nie jest objęty klasycznym modelem dziedziczenia lub wielokrotnego dziedziczenia.

  • Przechowywanie obiektów historycznych: Role idealnie nadają się do przechowywania obiektów historycznych nie powodując przy tym anomalii z unikalnością identyfikatorów obiektów. Np. można łatwo zapisać fakt, że Kowalski był już kiedyś dwa razy studentem i obecnie jest studentem. Ten aspekt powoduje zasadnicze trudności w innych modelach, gdyż stawia projektantów przed dylematem: czy obiekt historyczny ma mieć stary identyfikator, zaś bieżący obiekt ma mieć nowy (co prowadzi do niespójności referencyjnej z aktualnymi obiektami) czy odwrotnie (co prowadzi do niespójności referencyjnej z historycznymi obiektami), czy też należy zastosować jakąś szczególną metodę do przechowywania obiektów historycznych.

  • Wieloaspektowe dziedziczenie: Klasa może być specjalizowana według wielu aspektów, np. według stosunku do zatrudnienia lub stosunku do wykształcenia. UML przykrywa tę cechę, ale jest ona nieobecna w narzędziach obiektowych, co prowadzi m.in. do efektu "eksplozji klas". Role automatycznie mają cechę wieloaspektowego dziedziczenia.

  • Warianty (unie): Cecha ta, wprowadzona m.in. w C++, CORBA i ODMG, prowadzi do wielu semantycznych i implementacyjnych problemów. Role przykrywają tę cechę, przez co staje się niepotrzebna.

  • Migracja obiektów: Role mogą pojawiać się i znikać dynamicznie, co w terminach klasycznych modeli obiektowych oznacza, że obiekt zmienia klasę (czyli "migruje") bez zmiany tożsamości. Dla klasycznych modeli jest to duży problem. W przypadku ról problem ten nie istnieje.

  • Spójność referencyjna: W przypadku ról związki mogą prowadzić do ról, a nie do całych obiektów. Np. jeżeli nawigujemy od obiektu Szkoła do obiektu Kowalskiego poprzez jego rolę Student, wówczas niedostępny jest atrybut Zar i metoda ZarNetto. Jest to znaczne uściślenie hermetyzacji.

  • Dynamiczne dziedziczenie: KlasaPrac nie dziedziczy statycznie z KlasaOsoba. Zamiast tego rola Prac dziedziczy dynamicznie z roli Osoba wszystkie cechy, włączając metody zawarte w klasie KlasaOsoba. Stwarza to nową sytuację dla przesłaniania i polimorfizmu.

  • Heterogeniczne, przecinające się kolekcje. W klasycznych modelach, np. w ODMG, jeżeli obiekt był elementem kolekcji, to nie mógł być jednocześnie elementem innej kolekcji. Jest to ograniczenie modelowania pojęciowego. Dynamiczne role posiadają naturalną zdolność modelowania heterogenicznych, przecinających się kolekcji. Np. można utworzyć rolę Pacjent, i tą rolę objąć ludzi i zwierzęta, oraz inną rolę ObiektyDzisiajAktualizowane obejmującą obiekty dowolnego typu. Kolekcje PacjentObiektyDzisiajAktualizowane są heterogeniczne i zachodzą na siebie. Jak pisaliśmy wcześniej, pojęcie kolekcji nie jest do końca spójne z zasadą zamienialności oraz zasadą otwarte-zamknięte. Dynamiczne role obiektów zapewniają wszystkie te możliwości, które dają kolekcje, nie wprowadzając przy tym niespójności.
  • Programowanie aspektowe (Aspect-Oriented Programming, AOP) i rozdzielenie aspektów [Kicz97]. AOP zajmuje się rozdzieleniem przecinających się aspektów (cross-cutting concerns) w celu umieszczenie każdego aspektu w odrębnym module programu (np. historię zmian, reguły bezpieczeństwa, wizualizację itd.). Dynamiczne role mają wiele zbieżności
    z AOP lub mogą być wykorzystane jako techniczne wspomaganie AOP.
Copyrights © 2006 PJWSTK
Materiały zostały opracowane w PJWSTK w projekcie współfinansowanym ze środków EFS.