Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc

Sooda – proste i potężne narzędzie O/RM

Trochę teorii

Dla tych, którzy nie spotkali się jeszcze z pojęciem O/RM krótkie wyjaśnienie. Skrót oznacza Object-relational mapping. Narzędzia tej klasy służą do odwzorowania relacyjnej bazy danych, na bliższe każdemu programiście obiekty. Mapowanie odbywa się najczęsciej jako odworowanie definicji klas na strukturę tabel, a każdy rekord takich tabel reprezentowany jest przez konkretny obiekt. Jest kilka powodów, dla których warto zastanowić się nad takim rozwiązaniem. W rozwiązaniach nie korzystających z O/RM, musimy używać dwóch zupełnie różnych języków programowania, a kod, gdzie język obiektowy przeplata się z SQL’em, jest ciężki do utrzymania i rozwijania,  nie wspominając o debuggowaniu.  Narzędzia klasy O/RM pozwalają uprościć i zautomatyzować proces rozwijania i utrzymywania aplikacji bazodanych.  Jednym z takich rozwiązań jest Sooda – Simple Object Oriented Data Access, autorstwa Jarka Kowalskiego.

Sooda oparta jest na mechanizmie generacji kodu (o tym trochę więcej za chwilę). W naturalny sposób odwzorowuje obiekty na tabele, wspiera również kolekcje mapując je na relacje 1-N i M-N, dodatkowo umożliwiając dziedziczenie i polimorfizm. Pozwala także osiągnąć wysoką wydajność, m.in poprzez zaawansowane cache’owanie obiektów,  "leniwe ładowanie" (ang. lazy loading), itp. Cała konfiguracja natomiast przechowywana jest w jednym miejscu, co jest dużą zaletą przy np. zapoznawaniu się z aplikacją opartą na Soodzie.

Do tej pory największą bolączką Soody, był brak kompletnej dokumentacji. Od paru dni na stronie domowej Soody, jest kompletna i bardzo szczegółowa dokumentacja – link możecie znaleźć na końcu artykułu. Mam nadzieję, że artykuł ten szczegółowo wyjaśni jak skonfigurować aplikację, żeby zaczęła korzystać z Soody i krok po kroku wprowadzi Was w świat O/RM. Ponieważ najlepiej uczyć się na przykładzie, stworzymy prostą ‘Wypożyczalnię Video’. Osoby obecne na pierwszym spotkaniu Warszawskiej Grupie .NET  z pewnością pamiętają ten przykład. Zacznijmy więc od początku.

Konfiguracja aplikacji

Na początek przyda nam się plik instalacyjny Soody, lub też źródła (odpowiednie adresy na końcu). Mając przykładową bazę danych:

 

chcielibyśmy otrzymać strukturę klas odpowiadającą naszej bazie danych taką, jak przedstawia nam diagram klas:

 

Podczas konfiguracji projektu będziemy korzystać z aplikacji SoodaStubGen, dostępnej w pakiecie instalacyjnym Soody. Ten mały program ma za zadanie z dwóch plików konfiguracyjnych *.soodaproject i schematu bazy danych w pliku XML, wygenerować klasy odpowiadające naszej bazie. Użyteczną funkcją tego programu, jest to, że potrafi zaktualizować projekt Visual Studio, dodając do odpowiedniego pliku *.csproj wygenerowane pliki. Kod wygenerowany przez Soodę (może to być C#, VB.NET, Boo i teoretycznie każdy język wspierający generację kodu ze struktur CodeDom) znajduje się głównie w tzw. Stubs’ach – zachęcam do zerknięcia, co w tym pliku się znajduje. Dodatkowo SoodaStubGen generuje puste klasy, w których będziemy umieszczać naszą logikę biznesową. Powyżej opisany schemat tworzenia kodu wygląda mniej więcej tak:

 

Najwięcej pracy będziemy mieli przy tworzeniu pliku SoodaSchema.xml, odpowiedzialnego za odwzorowanie klas na tabele w bazie danych.  Pierwszą rzeczą jaką musimy umieścić w tym pliku, jest definicja źródła danych – w tej chwili jedynym wspieranym źródłem jest relacyjna baza danych. Aby zdefiniować źródło danych należy na początku pliku SoodaSchema.xml dodać element:

 <datasource name="default" type="Sooda.Sql.SqlDataSource" />

Podstawową tabelą, którą będziemy używać w naszej wypożyczalni jest tabela Video. Zmapujmy więc ją na interesującą nas klasę tak jak poniżej (pełne mapowanie na końcu artykułu):

    1 <class name="Video">

    2     <table name="Video">

    3         <field name="Id" type="Integer" primaryKey="true" nullable="false" />

    4         <field name="Title" type="String" nullable="false" />

    5         <field name="Category" type="Integer" references="VideoCategory" prefetch="1" />

    6         <field name="Status" type="Integer" references="VideoStatus" prefetch="1" />

    7         <field name="RentedOutTo" dbcolumn="rented_out_to" type="Integer" references="Customer" />

    8         <field name="RentedDate" dbcolumn="rented_date" type="DateTime" nullable="true" />

    9         <field name="ReturnedDate" dbcolumn="returned_date" type="DateTime" nullable="true" />

   10         <field name="DirectedBy" dbcolumn="director" type="Integer" references="Artist" prefetch="1" />

   11         <field name="YearOfProduction" dbcolumn="year_of_production" type="Integer" />

   12     </table>

   13 </class>

 

Podstawowym elementem jest class oparty na jednej lub wielu tabelach (możliwe jest również oparcie wielu klas na jednej tabeli) w bazie danych o nazwie „Video” (<table name="Video">). Każdej kolumnie w tabeli odpowiada jedna właściwość:

<field name="RentedDate" dbcolumn="rented_date" type="DateTime" nullable="true" />

Każda taka właściwość  ma oznaczony typ, zazwyczaj taki jak typ w bazie danych, nazwę – niekoniecznie taką samą i atrybut mówiący czy kolumna może być pusta. Poniżej wymieniono najczęściej spotykane typy pól (są też inne np. typ Image – zachęcam do lektury dokumentacji)

·       String - dodatkowo należy wyspecyfikować parametr size dla właściwości

·       Integer

·       DateTime

·       BooleanAsInteger – bardzo ciekawy typ, w bazie danych reprezentowany jako int (o wartości 1 lub 0), natomiast właściwość będzie przedstawiona jako typ boolean.

 

Przynajmniej jedna z właściwości musi być kluczem głównym (primaryKey="true"). Sooda wspiera również złożone klucze główne.  Jak łatwo na pełnym mapowaniu część właściwości ma zdefiniowany parametr references. Użycie go, spowoduje wygenerowanie właściwości  będącej referencją do innej klasy – czyli tak naprawdę klucza obcego np.  w klasie Video, właściwość Status jest tak naprawdę referencją do klasy VideoStatus.

Sooda wspiera również kolekcje jeden do wielu i wiele do wielu. Te pierwsze definiuje się poprzez element:

·        <collectionOneToMany> - reprezentowana jako referencja i kolekcja (np. dla klasy Video zdefiniowana jest kolekcja przetrzymująca hisorię wypożyczeń (VideoHistory)

·        <collectionManyToMany> - reprezentowana jako para kolekcji - dla Video jest to kolekcja aktorów, przypisanych do filmu (Actors2Video). W odróżeniu od klas, relacja jest przedstawiona w pliku mapującym, jako <relation>, gdzie klucze główne są referencjami

 

Przypuśćmy, że chcielibyśmy, aby  w naszej aplikacji oprócz danych klientów, przetrzymywać również dane pracowników, którzy mogliby wypożyczać filmy po specjalnych cenach. Aby nie dublować funkcjonalności w dwóch różnych obiektach (klient i pracownik). Najrozsądniejszym rozwiązaniem jest dziedziczenie klasy pracownik z klasy klient, lub stworzenie klasy abstrakcyjnej, z której dziedziczyłyby te dwie. Sooda wspomaga nas przy takich zadaniach, umożliwiając zdefinowanie pola (selektora), które będzie używane do odróżniania konkretnych podklas danej klasy. Najlepiej to zrozumieć na przykładzie naszej aplikacji. Klasa Person ma zdefniowany selektor: subclassSelectorField="Type". Na podstawie wartości pola Type, rekord z bazy danych będzie zwrócony jako obiekt tej, albo innej klasy. Klasy Customer i Employee dziedziczą z  klasy Person (inheritFrom="Person") na podstawie różnych wartości pola Type (wartość zdefiniowana przez atrybut subclassSelectorValue klasy). Klasa , która nie ma zdefinowanego subclassSelectorValue, będzie klasą abstrakcyjną.

Ostatnią rzeczą, o której chciałbym wspomnieć , są obiekty wyróźnione. Prawdziwą udręką podczas rozwijania aplikacji jest moment, gdy podczas przeglądania zapytania do bazy danych, nie pamiętamy co oznacza wartość 1 czy 2 w polu Status – trzeba przegrzebywać się przez ustalenia, dokumentację, czasem przeglądać zawartość bazy danych. Obiekty wyróżnione mają nam w tym pomóc. Wystarczy zdefiniować dla klasy odpowiednie elementy <const> mówiące, że rekord z tabeli, np. VideoStatus  o kluczu głównym 1 oznacza film wypożyczony (RentedOut).  Od tej pory będzie można poprzez proste wywołanie w kodzie, wyciągnąć ten rekord, w celu użycia go w zapytaniu, porównaniu, itp – ale o tym za chwilę.

Generowanie kodu

Mamy już więc  plik mapujący. W tym momencie będziemy potrzebować SoodaStubGen.exe. Zadaniem tego programu jest wygenerowanie na podstawie przygotowanego przez nas pliku, odpowiednich klas, z właściwościami, kolekcjami, obiektami wyróżnionymi itp. oraz wygenerowanie pustych klas, w których umieszczać będziemy całą funkcjonalność. SoodaStubGen ma wiele parametrów wywołania. Zamiast podawania długiej komendy łatwiej  będzie przygotować plik *.soodaproject – mały pliku xml, w którym zostaną umieszczone te parametry. Dzięki niemu będzie nam łatwiej przekonfigurowywać aplikację. Zawartość przykładowego pliku wygląda np tak:

    1 <?xml version="1.0" encoding="utf-8"?>

    2 <sooda-project xmlns="http://www.sooda.org/schemas/SoodaProject.xsd">

    3     <schema-file>SoodaSchema.xml</schema-file>

    4     <language>c#</language>

    5     <output-namespace>VideoRental</output-namespace>

    6     <output-path>.</output-path>

    7     <nullable-representation>SqlType</nullable-representation>

    8     <not-null-representation>Raw</not-null-representation>

    9     <with-indexers>false</with-indexers>

   10     <with-typed-queries>true</with-typed-queries>

   11     <embedded-schema-type>Binary</embedded-schema-type>

   12     <external-projects>

   13         <project type="vs2005" file="VideoRental.csproj"/>

   14     </external-projects>

   15 </sooda-project>

 

Najważniejsze z  parametrów:

  • schema-file – nazwa pliku mapującego;
  • language – język w którym kod zostanie wygenerowany (C# jest moim zdaniem najlepszym rozwiązaniem, gdyż tylko ten język pozwala na zastosowanie zapytań typowanych, o których za chwilkę opowiem);
  • output-namespace – przestrzeń nazw dla wygenerowanych klas;
  • output-path – miejsce, w którym pliki zostaną zapisane;
  • nullable-representation – reprezentacja właściwości mogących przyjmować wartość NULL. Możliwe jest użycie:
        o       Boxed – w tym przypadku tracimy informacje o typie, a właściwości będą zwracane jako typ object,

o       SqlType – użycie specjalnych typów z przestrzeni nazw System.Data.SqlTypes,

o       Raw – dane są przetrzymywane jako ‘standardowe’ typy,

o       Nullable – użyte będą typy Nullable z .NET 2.0,

o       RawWithIsNull – rozszerzenie opcji Raw. Dla każdej właściwości zostaną wygenerowane metody IsNull zwracające wartość prawda / fałsz.

  • not-null-representation – podobnie jak wyżej, tym razem dla właściwości nienullowalnych;
  • with-indexers – włącza lub wyłącza, indeksery dla wygenerowanych list;
  • with-typed-queries – włącza lub wyłącza używanie zapytań typowanych;
  • external-projects – pozwala zaktualizować pliki projektów VS2005 i VS2003 o wygenerowane pliki

 

Teraz musimy uruchomić SoodaStubGen podając jako parametr nazwę naszego pliku *.soodaproject. Zalecane jest ustawienie w projekcie Visual Studio w pre-build-event command line wywołania SoodaStubGen np. w ten sposób:

$(SolutionDir)\Sooda\SoodaStubGen.exe $(ProjectDir)VideoRental.soodaproject

Spowodouje to wywołanie SoodaStubGen z podktalogu Sooda, a jako parametr należy podajć plik VideoRental.soodaproject z katalogu projektu Wygenerowanie w ten sposób kodu, spowoduje wyświetlenie ostrzeżenia w Visual Studio, że plik projektu został zmieniony i należy go ponownie załadować. Dwie rzeczy, na które warto zwrócić uwagę:

1.      SoodaStubGen rozpoznaje, czy plik mapujący się zmienił – wówczas generuje jeszcze raz plik / pliki szkieletowe. Nie rozpoznaje jednak czy zmienił się plik *.soodaproj. W przypadku, gdy zmienimy coś w tym pliku, najlepiej jest zapisać plik mapującytak, aby zmieniła się data ostatniej modyfikacji pliku.

2.      SoodaStubGen potrafi dodawać nowo wygenerowane pliki do pliku projektu Visual Studio – jednak, co jest oczywiste, nie potrafi tych plików usuwać, w momencie gdy zrezygnujemy z jakiejś klasy lub zmienimy jej nazwę. Wówczas musimy taki plik usunąć ręcznie.

 

Zostały nam jeszcze dwie rzeczy do zrobienia - zdefiniowane connectionString i wskazanie, z jakiego schematu mapowania będzie korzystać Sooda. Wskazanie łańcucha połączeniowego może być zrobione na wiele sposobów - najprostszym będzie wykorzystanie pliku konfiguracyjnego App.config, lub specjalnego pliku Sooda.config.xml. W pierwszym przypadku wystarczy App.config uzupełnić o poniższą zawartość:
 

<!-- Wersja dla App.config -->

    1 <configuration>

    2     <appSettings>

    3         <add key="default.connectionString"

    4             value="Integrated Security=true;Server=.;Database=MyDatabase"/>

    5         <add key="default.sqlDialect" value="mssql"/>

    6     </appSettings>

    7 </configuration>

 
<!-- Wersja dla Sooda.config.xml -->

    1 

    2 <configuration>

    3     <default>

    4         <connectionString>

    5             Integrated Security=true;Server=.;Database=MyDatabase

    6         </connectionString>

    7         <sqlDialect>

    8             mssql

    9         </sqlDialect>

   10     </default>

   11 </configuration>

W drugim przypadku możemy wszystkie Soodowe ustawienia trzymać w oddzielnym pliku xml. Aby go użyć, należy w pliku AssemblyInfo.cs dodać tę linijkę:

[assembly: SoodaConfig(XmlConfigFileName = "Sooda.config.xml")]

Na Rysunku powyżej pokazana jest przykładowa zawartość tego pliku, ustawiająca connectionString. W samej konfiguracji możemy definiować jeszcze wiele parametrów dla naszej aplikacji, jak dialekt sql (specyficzne dla silnika bazodanego generowanie sql'a), rodzaj bazy danych, itp. Wskazanie schematu mapowania jest potrzebne dla prawidłowego działania Soody. Znów mamy możliwość zrobienia tego na kilka sposobów. Ponownie najprościej będzie dodać do AssemblyInfo.cs tę linijkę:

[assembly: SoodaStubAssembly(typeof(MojaPrzestrzenNazw._DatabaseSchema))]

Programowanie z użyciem Soody

W momencie wygenerowania klas, pozostaje nam tylko zakodowanie funkcjonalności naszej aplikacji. Na początku może zróbmy coś prostego:  wypiszmy na ekran wszystkie filmy video, które mamy w bazie(skrypt zakładający bazę i wypełniający ją przykładowymi danymi znajdziecie pod jednym z linków podanych na końcu). W tym celu użyjemy metody, którą posiadają wszystkie obiekty Soodowe: GetList. Po pierwsze, aby używać Soody, musimy stworzyć transakcję – wszystkie obiekty żyją tylko w obrębie transakcji, wszelkie operacje na obiektach po jej zakończeniu mogą skutkować wyjątkiem. Utworzenie transakcji jest proste – wystarczy użyć poniższej składni:

using (SoodaTransaction t = new SoodaTransaction()) { /* nasz kod */ }
 

Jak więc z tabeli Video pobrać wszystkie rekordy i wypisać np tytuł razem z rokiem produkcji? Nic prostszego:

 
using
(SoodaTransaction t = new SoodaTransaction())
{
    // SQL: select * from Video;
    VideoList list = Video.GetList(true);

    foreach (Video v in list)
    {
       Console.WriteLine("Tytuł {0}, Rok produkcji: {1}\n",
       
     
v.Title, v.YearOfProduction);
    }
}

Wykorzystaliśmy tutaj właśnie metodę GetList. Przekazany parametr true pobierze wszystkie rekordy bez jakiegokolwiek zawężania listy. Każda klasa Soodowa ma odpowiadającą mu klasę KlasaList, posiadającą standardowe metody spotykane w listach, takie jak Add, Remove, Contains, a także kilka innych użytecznych metod.
 

Załóżmy, że chcielibyśmy zawęzić nasze poszukiwania do filmów wypożyczonych, które mają w tytule ‘Harry’. Wystarczy znów użyć metody GetList, tym razem jako parametr podając wyrażenie zawężające listę:

 
VideoList
list = Video.GetList(
      VideoField
.Title.Like("Harry%") &&
      VideoField
.Status == VideoStatus.RentedOut);

Jako parametr użyliśmy tutaj zapytań typowanych (dostępnych tylko dla języka C#). Podobnie jak w przypadku list, tak i tutaj, dla każdej klasy generowane są dodatkowe KlasaField – używane właśnie w tego rodzaju zapytaniach. Budowanie zapytania, wygląda jak budowanie zwykłego warunku logicznego. Co ciekawe, w dowolny sposób możemy przechodzić po właściwościach obiektów, np.warunek:

VideoField.Status == VideoStatus.RentedOut (zwróćcie uwagę na użycie obiektu wyróżnionego VideoStatus.RentedOut)

można również zapisać w ten sposób:

VideoField.Status.Name == "Rented Out".

W przypadku zapytań typowanych w bonusie dostajemy IntelliSense w VS podczas budowania zapytań (przy dostępie do metod i właściwości, IntelliSense jest oczywiście dostępny dla każdego języka), a także jak sama nazwa wskazuje kontrolę typów na poziomie kompilacji. W innych językach, które wspiera Sooda, jak też i C#, można użyć zapytań nietypowanych:

 

    1 // do zapytań nietypowanych używamy SoodaWhereClause

    2 VideoList list = Video.GetList(new SoodaWhereClause(

    3     "Title like {0} and Status == {1}",

    4     "HarryPotter", VideoStatus.Available)

    5 );
 

Zróbmy coś więcej. Wypiszmy wszystkie filmy, w których gra Emma Watson. Można zrobić to na dwa sposoby. Pierwszy sposób to pobranie odpowiedniego obiektu typu Artist i wyświetlenie wszystkich filmów z kolekcji powiązanej z tą aktorką. Problem jest taki, że w tym momencie w pliku mapującym dla klasy Artist nie mamy zdefiniowanej takiej kolekcji. Musimy dodać tę definicję kolekcji do klasy Artist:
 

<collectionManyToMany name="Videos" relation="Actor2Video" masterField="1" />
 

Po skompilowaniu projektu, możemy użyć następującego kodu:

using (SoodaTransaction t = new SoodaTransaction())
{

    // Szukamy Emmy Watson

    Artist emmaWatson = Artist.LoadSingleObject(

ArtistField.Name == "Emma Watson");

 

    foreach (Video v in emmaWatson.Videos)

        // wypisanie Danych

}

 

LoadSingleObject pobiera z bazy danych pojedynczy obiekt. Jeżeli żaden obiekt nie będzie odpowiadał podanym warunkom, zostanie zwrócony null, jeżeli więcej niż jeden - zostanie zgłoszony wyjątek. Drugim sposobem jest pobranie tych obiektów typu  Video, które w kolecji aktorów, zawierają szukanego przez nas artystę:

 
Artist
emmaWatson = Artist.LoadSingleObject(
    ArtistField
.Name == "Emma Watson");

 
VideoList list = Video.GetList(
    VideoField
.Actors.Contains(emmaWatson));
 

Niżej można zobaczyć jeszcze bardziej rozbudowane zawężenie listy:

 

    1 VideoList list = Video.GetList(

    2     VideoField.YearOfProduction >= 2000

    3     && VideoField.YearOfProduction <= 2004

    4     && VideoField.Status == VideoStatus.RentedOut

    5     && VideoField.RentedOutTo.Email.Like("%@customer.com")

    6     && VideoField.DirectedBy.Name.Like("Chris%")

    7     && VideoField.History.Count > 1);

 

Dużo mówimy tu o pobieraniu obiektów, a co z tworzeniem nowych? Chcemy dodać nowy film, razem z aktorami. Nic prostszego – wystarczy stworzyć tylko obiekt w transakcji Soodowej, a następnie taką transakcje zapisąc, przy użyciu metody Commit(),  tak jak tutaj:

 

    1 using (SoodaTransaction t = new SoodaTransaction())

    2 {

    3     Artist emmaWatson = Artist.LoadSingleObject(ArtistField.Name == "Emma Watson");

    4 

    5     Video v = new Video();

    6     v.Title = "Harry Potter i Czara Ognia";

    7     v.Status = VideoStatus.Available;

    8     v.YearOfProduction = 2005;

    9     v.Actors.Add(emmaWatson);

   10 

   11     // ten warunek zwróci nam właśnie utworzony film

   12     VideoList v = v.GetList(VideoField.Title == "Harry Potter i Czara Ognia");

   13 

   14     // commitujemy transakcję

   15     t.Commit();

   16 }

Bardzo interesujące jest to, że przed zapisaniem transakcji , możemy utworzony obiekt już pobrać. Z pewnością część z Was zauważyła, że nie podajemy jawnie klucza głównego dla tego filmu. Sooda domyślnie sama generuje poprawne klucze główne, przechowując je w specjalnej tabeli KeyGen o strukturze takiej jak na tutaj:

 

Uaktualnienie rekordu, jest równie proste – wystarczy nadać nowe wartości właściwościom.  Natomiast aby usunąć biekt należy wywołać metodę MarkForDelete().

Krótkie podsumowanie

Nie zdołaliśmy nawet zacząć się przekopywać przez funkcjonalność drzemiącą w Soodzie. Nie wykorzystaliśmy np. triggerów – metod pełniących tę samą funkcję co triggery w bazie danych. Zainteresowanych, a mam nadzieję, że tacy będą po przeczytaniu tego artykułu, odsyłam do dokumentacji. Na sam koniec chciałem przedstawić pomocnika, który ma automatycznie tworzyć plik mapujący, plik konfiguracyjny *.soodaproj i zaktualizowanie projektu Visual Studio. Po zainstalowaniu Soody 1.0 w Menu Start, będziemy mogli dodać Soodę do projektu Visual Studio 2005. Po wybraniu tej opcji, uruchomi się mały wizard, prowadzący nas za rękę po konfiguracji Soody


 

Po co więc ten rozbudowany artykuł? Program nie wygeneruje pliku mapującego z dziedziczeniem, czy obiektami wyróżnionymi – a z pewnością warto wiedzieć jak ten plik zmieniać. Życzę miłej zabawy i pracy z Soodą. Nie zniechęcajcie się – czas poświęcony na konfigurację aplikacji i  poznanie Soody zostanie Wam zwrócony z nawiązką podczas tworzenia aplikacji.

Użyteczne linki:

1. Strona domowa Soody z pełną dokumentacją i plikami instalacyjnymi: http://sooda.org
2. Prezentacja Jarka Kowalskiego na temat Soody razem z przykładowym kodem: http://svn.sooda.org/repos/sooda/trunk/presentations/
3. Repozytorium Soody: http://svn.sooda.org/repos/sooda/

Pełny schemat mapowania:

    1 <schema xmlns="http://www.sooda.org/schemas/SoodaSchema.xsd">

    2   <datasource name="default" type="Sooda.Sql.SqlDataSource" />

    3 

    4     <class name="Video">

    5         <table name="Video">

    6             <field name="Id" type="Integer" primaryKey="true" nullable="false" />

    7             <field name="Title" type="String" nullable="false" />

    8             <field name="Category" type="Integer" references="VideoCategory" prefetch="1" />

    9             <field name="Status" type="Integer" references="VideoStatus" prefetch="1" />

   10             <field name="RentedOutTo" dbcolumn="rented_out_to" type="Integer" references="Customer" />

   11             <field name="RentedDate" dbcolumn="rented_date" type="DateTime" nullable="true" />

   12             <field name="ReturnedDate" dbcolumn="returned_date" type="DateTime" nullable="true" />

   13             <field name="DirectedBy" dbcolumn="director" type="Integer" references="Artist" prefetch="1" />

   14             <field name="YearOfProduction" dbcolumn="year_of_production" type="Integer" />

   15         </table>

   16         <collectionOneToMany name="History" class="VideoHistory" foreignField="Video" />

   17         <collectionManyToMany name="Actors" relation="Actor2Video" masterField="0" />

   18     </class>

   19 

   20   <class name="Person"

   21         subclassSelectorField="Type">

   22     <table name="Person">

   23       <field name="Id" type="Integer" primaryKey="true" nullable="false" />

   24       <field name="Type" type="Integer" nullable="false" />

   25       <field name="Name" type="String" nullable="false" />

   26     </table>

   27   </class>

   28 

   29   <class name="Employee"

   30         inheritFrom="Person"

   31         subclassSelectorValue="1">

   32     <table name="Employee">

   33       <field name="Id" type="Integer" primaryKey="true" nullable="false" />

   34       <field name="Login" type="String" nullable="false" />

   35     </table>

   36     <const name="Edward" key="1" />

   37     <const name="Admin" key="2" />

   38   </class>

   39 

   40   <class name="Customer"

   41         inheritFrom="Person"

   42         subclassSelectorValue="2">

   43     <table name="Customer">

   44       <field name="Id" type="Integer" primaryKey="true" nullable="false" />

   45       <field name="Email" type="String" nullable="false" />

   46       <field name="Address" type="String" nullable="false" />

   47     </table>

   48   </class>

   49 

   50   <class name="VideoHistory">

   51     <table name="VideoHistory">

   52       <field name="Id" type="Integer" primaryKey="true" nullable="false" />

   53       <field name="Video" dbcolumn="video_id" type="Integer" references="Video" nullable="false" />

   54       <field name="Person" dbcolumn="person_id" type="Integer" references="Person" nullable="false" onDelete="Cascade"/>

   55       <field name="TimeStamp" dbcolumn="time_stamp" type="DateTime" nullable="true" />

   56       <field name="ActionType" dbcolumn="action_type" type="Integer" references="VideoHistoryActionType" nullable="false" />

   57       <field name="Comment" type="String" nullable="false" />

   58     </table>

   59   </class>

   60 

   61   <class name="VideoCategory">

   62     <table name="VideoCategory">

   63       <field name="Id" type="Integer" primaryKey="true" nullable="false" />

   64       <field name="Name" type="String" nullable="false" />

   65     </table>

   66   </class>

   67 

   68   <class name="VideoStatus">

   69     <table name="VideoStatus">

   70       <field name="Id" type="Integer" primaryKey="true" nullable="false" />

   71       <field name="Name" type="String" nullable="false" />

   72     </table>

   73     <const name="RentedOut" key="1" />

   74     <const name="Available" key="2" />

   75     <const name="Damaged" key="3" />

   76     <const name="Lost" key="4" />

   77   </class>

   78 

   79   <class name="VideoHistoryActionType">

   80     <table name="VideoHistoryActionType">

   81       <field name="Id" type="Integer" primaryKey="true" nullable="false" />

   82       <field name="Name" type="String" nullable="false" />

   83     </table>

   84     <const name="RentedOut" key="1" />

   85     <const name="Returned" key="2" />

   86     <const name="Lost" key="3" />

   87     <const name="Damaged" key="4" />

   88     <const name="Created" key="5" />

   89     <const name="Deactivated" key="6" />

   90   </class>

   91 

   92   <class name="Artist">

   93     <table name="Artist">

   94       <field name="Id" type="Integer" primaryKey="true" nullable="false" />

   95       <field name="Name" type="String" nullable="false" />

   96     </table>

   97     <const name="ChuckNorris" key="1" />

   98     <const name="ValKilmer" key="2"/>

   99     <const name="OmarSharif" key="3"/>

  100   </class>

  101 

  102   <relation name="Actor2Video">

  103     <table name="Actor2Video">

  104       <field name="Actor" dbcolumn="actor_id" type="Integer" references="Artist" />

  105       <field name="Video" dbcolumn="video_id" type="Integer" references="Video" />

  106     </table>

  107   </relation>

  108 

  109 </schema>

 

Opublikowane 28 stycznia 2007 20:32 przez yoshi
Filed under:

Komentarze:

# Zine-cast #2

30 marca 2007 11:57 by ZineCast

W tym odcinku Marcin Krupiński (yoshi) zdradza sekrety Soody ... swojej żonie Agnieszce :)

Komentarze anonimowe wyłączone