JDO – projekce do relační databáze
Dosud jsme při výkladu JDO zůstávali převážně na objektové úrovni a zcela výjimečně jsme se dotkli toho, jak se persistentní objekty ve skutečnosti ukládají do relační databáze. I když při běžném používání lze tuto práci ponechat na implementaci JDO, z hlediska případných optimalizací a také pro případ, že bychom potřebovali přímo přistupovat k tabulkám databáze, je vhodné přinejmenším vědět, jak jsou naše data v databázi organizována.
Reprezentace tříd v objektové databázi
V zásadě platí, že každé persistentní třídě odpovídá jedna tabulka relační databáze. Každá relační tabulka má vždy nejméně jeden sloupec, který obsahuje jednoznačný identifikátor objektu. V případě aplikační identity se složeným primárním klíčem (popsáno v předchozích článcích) může být těchto sloupců více.
Každé persistentní vlastnosti třídy odpovídá jeden sloupec tabulky. Přímo do tabulky se ukládají hodnoty primitivních datových typů, jejich příslušných objektových variant a třídy Date
. Ostatní datové typy vyžadují speciální zacházení.
Název každé tabulky se odvozuje z názvu třídy (například JPOX používá implicitně název třídy, který může být v závislosti na konfiguraci převedený na velká nebo malá písmena a podobně). V některých případech je vhodné název tabulky zcela změnit, například pokud název třídy koliduje s některým klíčovým slovem SQL a mohlo by dojít k problémům při manipulaci s tabulkou v relační databázi. Proto jednotlivé implementace umožňují zadat jiný název tabulky. Protože standard JDO 1.0.1 tuto problematiku opět neřeší, používají jednotlivé implementace svoje vlastní rozšíření. Například JPOX používá rozšíření table-name
:
<package name=“demo“>
<class name=“osoba“ identity-type=“datastore“>
<extension vendor-name=“jpox“
key=“table-name“ value=“PERSON“/>
<field name=“jmeno“>
<extension vendor-name=“jpox“ key=“length“ value=“max 50″/>
</field>
…
</class>
</package>
Stejná pravidla platí pro názvy sloupců tabulek, které se obdobně odvozují z názvů jednotlivých vlastností tříd. V případě změny je u JPOX možno použít rozšíření column-name
:
<package name=“demo“>
<class name=“osoba“ identity-type=“datastore“>
<extension vendor-name=“jpox“
key=“table-name“ value=“PERSON“/>
<field name=“jmeno“>
<extension vendor-name=“jpox“ key=“column-name“ value=“NAME“/>
<extension vendor-name=“jpox“ key=“length“ value=“max 50″/>
</field>
…
</class>
</package>
U jednotlivých implementací se názvy těchto rozšíření mohou lišit a je opět třeba hledat v dokumentaci. Standard JDO 2.0 by měl tyto vlastnosti sjednotit.
Vnořené objekty
Vnořené objekty (second class objects, SCO) nejsou ukládány ve zvláštní tabulce, ale v rámci tabulky třídy, ve které jsou vnořené. Že je objekt vnořený, je uvedeno v metadatech pomocí atributu embedded
elementu <field>
. Například vložený objekt třídy Adresa
se v metadatech specifikuje následujícím způsobem:
<package name=“demo“>
<class name=“osoba“ identity-type=“datastore“>
<field name=“adresa“ embedded=“true“/>
…
</class>
</package>
Primitivní datové typy a odpovídající třídy jsou vnořené implicitně a ukládají se do sloupce s odpovídajícím datovým typem. U ostatních datových typů se využívá toho, že každý persistentní objekt je serializovatelný – může být převeden na pole bajtů a z něj může být opět rekonstruován. Ukládané vložené objekty se proto převedou na serializovaný tvar a uloží do sloupce typu BLOB (Binary Large Object) nebo podobného podle možností příslušného databázového systému.
Pokud objekt není vnořený, ukládá se do tabulky příslušné k jeho třídě a se svým vlastnickým objektem se propojuje prostřednictvím relací.
Relace
Jak jsme již zmínili v předchozích článcích, relace lze modelovat prostřednictvím různých mapování.
U relace 1:1 je možné buď jednostranné nebo oboustranné mapování. U jednostranného mapování objektů tříd A
a B
obsahuje třída A
jako atribut objekt třídy B
. Pokud se nejedná o vnořený objekt, projeví se to v relační tabulce třídy A
tak, že bude obsahovat sloupec nebo sloupce s cizím klíčem do tabulky třídy B
. V případě oboustranného mapování bude navíc tabulka třídy B
obsahovat sloupec s cizím klíčem do tabulky třídy A
.
U relací 1:N se používá buď normální nebo oboustranné inverzní mapování. V prvním případě objekt třídy A
obsahuje kolekci objektů třídy B
. K tabulkám tříd A
a B
proto přibude třetí tabulka, reprezentující kolekci, která bude obsahovat sloupce s cizími klíči do tabulek A
a B
a bude tedy vzájemně přiřazovat objekty třídy A
a objekty třídy B
. U oboustranného inverzního mapování je tato tabulka nahrazena dalším sloupcem v tabulce třídy B
, který má význam cizího klíče do tabulky třídy A
.
Relace M:N používají na každé straně normálního mapování, každá ze tříd A
a B
obsahuje kolekci objektů opačné třídy. Kromě tabulek pro obě třídy tedy dostaneme ještě dvě tabulky pro kolekce.
Reprezentace kolekcí
Reprezentace kolekcí v podstatě představuje stejný problém jako reprezentace relace 1:N. V reprezentaci jednotlivých základních kolekcí Set
, List
a Map
jsou však drobné rozdíly. Předpokládejme, že kolekce je obsažena jako atribut ve třídě vlastníka A
a obsahuje prvky třídy B
. Jak jsme již zmínili, třída prvků obsažených v kolekci musí být specifikována v metadatech třídy A
, aby mohly být prvky kolekce správně uloženy.
U všech typů kolekcí jsou možné dva druhy mapování na tabulky. Takzvané normální mapování odpovídá normálnímu mapování relací 1:N, inverzní mapování odpovídá oboustrannému inverznímu mapování. V obou případech se vytváří po jedné relační tabulce pro třídy A
a B
. U normálního mapování se navíc vytváří propojovací tabulka přiřazující objekty třídy B
objektům třídy A
. U inverzního mapování je propojovací tabulka nahrazena sloupcem s cizím klíčem v tabulce B
.
U množiny (Set
) při normálním mapování obsahuje spojovací tabulka sloupce s primárními klíči tabulek A
a B
, které se eventuálně mohou skládat z více sloupců. Všechny sloupce spojovací tabulky tvoří její primární klíč, což vylučuje duplicitu řádků a zajišťuje, že množina nebude obsahovat duplicitní prvky, jak vyplývá z její definice.
Normální mapování u seznamu (List
) je řešeno obdobným způsobem, spojovací tabulka ovšem navíc obsahuje sloupec index
, který určuje pořadí objektů v seznamu. Sloupec index
je součástí primárního klíče tabulky, takže seznam připouští duplicitu objektů na různých pozicích v seznamu.
Pro mapy (Map
) vzniká tabulka pro třídu vlastníka A
, pro třídu hodnot B
a pro třídu klíčů C
a dále spojovací tabulka, přiřazující trojice A-B-C
. Tato tabulka obsahuje primární klíče všech tří tabulek tříd.
Inverzní mapování všech tří kolekcí je přímočaré a do relačních tabulek se promítá stejným způsobem jako oboustranná inverzní relace 1:N. U seznamů navíc v tabulce třídy B
(třída prvků seznamu) přibude sloupec obsahující pořadí prvku v seznamu. Je nutno si uvědomit, že toto řešení znemožňuje duplicitu prvků v seznamu – jeden objekt se nemůže současně nacházet na různých pozicích v seznamu. Pokud tuto vlastnost vyžadujeme, je nutno použít normálního mapování. U mapy je v tabulkách hodnot (B
) i klíčů (C
) obsažen cizí klíč do tabulky vlastníka A
).
Dědičnost tříd
V případě odvozených tříd se vytváří v relační databázi tabulka pro každou odvozenou třídu. Mějme dvě třídy definované následujícím způsobem:
public class Osoba
{
protected String jmeno;
protected String prijmeni;
protected int vek;
}
public class Zamestnanec extends Osoba
{
/** Od kdy je u nás zaměstnán */
private Date zamestnanOd;
}
V tomto případě vzniknou dvě tabulky – tabulka Osoba
se sloupci jmeno
, prijmeni
a vek
a tabulka Zamestnanec
s jedním sloupcem zamestnanOd
. Navíc má každá tabulka jeden nebo více sloupců obsahujících identifikátor objektu (primární klíč).
Uložíme-li do databáze persistentní objekt třídy Osoba
, projeví se jako nový řádek v tabulce Osoba
. Uložíme-li objekt třídy zaměstnanec, projeví se jako nový řádek v obou tabulkách – hodnota vlastnosti zamestnanOd
se uloží do tabulky Zamestnanec
, hodnoty ostatních vlastností se uloží do příslušných sloupců tabulky Osoba
, přičemž příslušné řádky v obou tabulkách budou mít stejný primární klíč. Z hlediska čtení objektů z databáze je pak vhodné si uvědomit, že načtení každého odvozeného objektu z relační databáze představuje provedení poměrně složitého dotazu se spojením (UNION) tabulek.
Souhrn
Tímto článkem jsme se dostali na závěr série o JDO. Ačkoli jsme v současnosti svědky takřka neomezené nadvlády relačních databází, vývoj zdá se pomalu ale jistě směřuje k databázím objektovým, i když jejich nástup je zatím poněkud opatrný. Proto, jakkoli dnes může přímé použití JDBC a jazyka SQL v Javovských aplikacích vypadat jako mnohem jednodušší a efektivnější řešení, použití univerzálního rozhraní pro persistenci objektů, které představují Java Data Objects, je investicí jednak do udržovatelnosti celé aplikace a jednak do budoucnosti, kdy bude možné s celou aplikací bezbolestně přejít na efektivní objektovou databázi v okamžiku, kdy se relační databáze odeberou na svoje zasloužené místo v historii informačních technologií.
Starší komentáře ke článku
Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.
Mohlo by vás také zajímat
-
OpenAI představilo novou funkci ChatGPT Search
6. listopadu 2024 -
Aukce CZ domén: Jak vydražit expirovanou CZ doménu?
12. června 2024 -
Vlastní web: Jak nainstalovat WordPress?
24. června 2024
Nejnovější
-
Jak rozšířit úložiště Macu za pětinovou cenu?
16. prosince 2024 -
Nové trendy v doménách pro osobní projekty – DIY, LIVING a LIFESTYLE
9. prosince 2024 -
Jak chránit webové stránky před Web/AI Scrapingem
27. listopadu 2024 -
Jaký monitor je nejlepší k novému Macu Mini?
25. listopadu 2024