Podkladem pro návrh struktury objektové databáze bývá obvykle diagram tříd, který popisuje jednotlivé třídy v aplikační doméně, jejich vlastnosti a vzájemné vztahy. Dosud jsme se při práci s JDO zaměřovali na jednotlivé třídy, v tomto článku se podíváme blíže na implementaci vztahů mezi třídami.

Objektový návrh aplikací dnes probíhá převážně s využitím jazyka UML (Unified Modelling Language). Pro návrh databáze je podstatný zejména diagram tříd, který v grafické podobě reprezentuje jednotlivé třídy a jejich vztahy. Vztahy mezi objekty dvou různých tříd A a B lze z pohledu objektové databáze rozdělit do následujících skupin:

  • Vztahy 1:1 – ke každému objektu třídy A přísluší právě jeden objekt třídy B.
  • Vztahy 1:N – ke každému objektu třídy A může příslušet více objektů třídy B. V diagramu tříd lze tento vztah dále konkretizovat, například zda je příslušných objektů třídy B jeden a více, či zda nemusí být ani jeden.
  • Vztahy M:N – ke každému objektu třídy A může příslušet více objektů třídy B a zároveň ke každému objektu třídy B může příslušet více různých objektů třídy A.

Jednotlivé typy vztahů se liší zejména způsobem jejich implementace v Javě a JDO a z pohledu úložiště objektů i způsobem vnitřní reprezentace, o které je v případě JDO nutné mít alespoň obecnou představu.

Vztahy 1:1

Vztahy 1:1 mohou být v zásadě dvojího druhu. První a běžnější variantou je takzvaný jednostranný (single-ended) vztah, kdy objekt třídy A obsahuje informaci o příslušném objektu třídy B. Druhou variantu představuje takzvaný oboustranný vztah, kdy navíc i objekt B obsahuje informaci o objektu třídy A, ke kterému přísluší. Uvažujme jednoduchý případ, kdy evidujeme pojistky povinného ručení motorových vozidel. U vztahu vozidlo – pojistka se zjevně jedná o vztah 1:1. Podívejme se na to, jak v praxi realizujeme jednostrannou i oboustrannou variantu tohoto vztahu.

Jednostranný vztah

Jako příklad si uveďme následující vztah tříd Vozidlo a Pojistka:

Jednostranný vztah 1:1

V tomto příkladě obsahuje třída Vozidlo položku pojistka, která obsahuje odpovídající objekt třídy Pojistka. Metadata pro obě třídy mohou vypadat například takto:

<package name=“ruceni“>
    <class name=“Vozidlo“ identity-type=“datastore“>
        <field name=“SPZ“ persistence-modifier=“persistent“>
            <extension vendor-name=“jpox“ key=“length“ value=“max 10″/>
        </field>
        <field name=“pojistka“ persistence-modifier=“persistent“>
        </field>
    </class>
    <class name=“Pojistka“ identity-type=“datastore“>
        <field name=“cislo“ persistence-modifier=“persistent“>
            <extension vendor-name=“jpox“ key=“length“ value=“max 50″/>
        </field>
        <field name=“castka“ persistence-modifier=“persistent“>
        </field>
        …
    </class>
</package>

Oboustranný vztah

V případě oboustranného vztahu přibude ve třídě Pojistka položka vozidlo, která bude obsahovat objekt reprezentující příslušné vozidlo:

Oboustranný vztah 1:1

Obdobně se nová položka vozidlo objeví i v metadatech. Oboustranné vztahy se doporučuje používat pouze v nezbytných případech a se značnou dávkou opatrnosti. Při vkládání takových objektů do databáze totiž nastává situace, kdy jeden z objektů je již v databázi a druhý dosud nebyl vložen, takže první objekt se odkazuje na neplatný objekt. Proto je vhodné nejprve vložit oba objekty do databáze s prázdnými referencemi na ostatní objekty a teprve následně nastavit hodnoty příslušných vlastností.

Vztahy 1:N

U vztahů 1:N je jednomu objektu třídy A přiřazeno více objektů třídy B, případně můžeme připustit i prázdnou množinu objektů třídy B (kardinalita vztahu je 0..N). Existují tři možnosti jak vyjádřit tento vztah v JDO:

  • Normální mapování – třída A obsahuje jako atribut kolekci objektů třídy B.
  • Jednostranné inverzní mapování – každý z objektů třídy B obsahuje jako atribut objekt třídy A, se kterým je v relaci.
  • Oboustranné inverzní mapování – kombinace předchozích dvou možností.

Podle konkrétních požadavků lze použít libovolnou kolekci podporovanou JDO, tedy množinu, seznam nebo mapu, či některou z jejich podtříd.

Normální mapování

Jedná se o nejběžnější druh mapování. Jeho hlavní výhodou je, že není třeba žádných zvláštních rozšíření JDO, zcela vystačíme s použitím kolekcí v té formě, jak byly popsány v předchozím článku. Ukažme si jednoduchý příklad. Mějme vztah majitel – vozidlo, kde jedno vozidlo má vždy jednoho majitele, ale jeden majitel může vlastnit více vozidel:

Normální vztah 1:N

Metadata pro tyto třídy budou v tomto případě vypadat následovně:

<package name=“demo“>
    <class name=“Osoba“ identity-type=“datastore“>
        <field name=“jmeno“ persistence-modifier=“persistent“>
            <extension vendor-name=“jpox“ key=“length“ value=“max 50″/>
        </field>
        <field name=“prijmeni“ persistence-modifier=“persistent“>
            <extension vendor-name=“jpox“ key=“length“ value=“max 50″/>
        </field>
        <field name=“vozidla“ persistence-modifier=“persistent“>
            <collection element-type=“Vozidlo“/>
        </field>
    </class>
    <class name=“Vozidlo“ identity-type=“datastore“>
        <field name=“SPZ“ persistence-modifier=“persistent“>
            <extension vendor-name=“jpox“ key=“length“ value=“max 10″/>
        </field>
    </class>
</package>

Drobným nedostatkem normálního mapování je, že v případě jeho projekce do relační databáze vznikají tři tabulky – první tabulka pro třídu vlastníka, druhá tabulka pro třídu prvků kolekce a třetí tabulka spojovací (join table), která vzájemně přiřazuje objekty těchto dvou tříd. Pokud z nějakého důvodu chceme vystačit se dvěma tabulkami, je třeba použít inverzní mapování.

Jednostranné inverzní mapování

U jednostranného inverzního mapování třída A (v předchozím příkladě třída Osoba) neuchovává žádné informace o přiřazených objektech třídy B. Naopak každý objekt třídy B obsahuje informaci o tom, ke kterému objektu třídy A přísluší. Tento typ relace není většinou implementací podporován, obvykle se upřednostňuje podpora oboustranného inverzního mapování, vůči kterému nepřináší jednostranné mapování žádné podstatné výhody.

Oboustranné inverzní mapování

Oboustranné mapování představuje kombinaci obou předchozích. Třída A obsahuje kolekci příslušejících objektů třídy B a zároveň každý prvek třídy B obsahuje jako atribut objekt třídy A, ke kterému přísluší. Předchozí příklad pro tuto variantu upravíme tak, že třídě Vozidlo přidáme atribut vlastnik. Dostaneme následující diagram:

Oboustranný inverzní vztah 1:N

V metadatech bude úprava poněkud složitější, protože musíme specifikovat, které z atributů jsou použity pro mapování. V našem případě jde o kolekci vozidla ve třídě Osoba a atribut vlastnik ve třídě Vozidlo. Metadata potom budou vypadat následovně:

<package name=“demo“>
    <class name=“Osoba“ identity-type=“datastore“>
        <field name=“jmeno“ persistence-modifier=“persistent“>
            <extension vendor-name=“jpox“ key=“length“ value=“max 50″/>
        </field>
        <field name=“prijmeni“ persistence-modifier=“persistent“>
            <extension vendor-name=“jpox“ key=“length“ value=“max 50″/>
        </field>
        <field name=“vozidla“ persistence-modifier=“persistent“>
            <collection element-type=“Vozidlo“>
                <extension vendor-name=“jpox“ 
                           key=“owner-field“ 
                           value=“vlastnik“/>

            </collection>
        </field>
    </class>
    <class name=“Vozidlo“ identity-type=“datastore“>
        <field name=“SPZ“ persistence-modifier=“persistent“>
            <extension vendor-name=“jpox“ key=“length“ value=“max 10″/>
        </field>
        <field name=“vlastnik“ persistence-modifier=“persistent“>
            <extension vendor-name=“jpox“ 
                       key=“collection-field“ 
                       value=“vozidla“/>
        </field>

    </class>
</package>

Jak je patrné z příkladu, tvar specifikace opět není standardizován v JDO 1.0.1 a využívá se proto proprietárních rozšíření. Příklad ukazuje tvar specifikace pro JPOX. Na jedné straně u kolekce zadáme jmeno „protějšího“ atributu pomocí rozšíření owner-field, na druhé straně pak naopak zadáme název kolekce pomocí rozšíření collection-field. V jiných implementacích JDO se tato rozšíření jmenují různě a je třeba je nalézt v dokumentaci. Například u komerční implementace Kodo (vendor-name="kodo") se používá inverse-owner místo owner-field a Speedo používá název reverse-field.

U oboustranného mapování není zapotřebí vytvářet v relační databázi spojovací tabulku, neboť vztahy objektů jsou uloženy přímo v atributech třídy B.

Relace M:N

Vztah M:N přiřazuje každému objektu třídy A více objektů třídy B a zároveň každému objektu třídy B více objektů třídy A. V tomto případě se obvykle používá mapování, které jsme v předchozích případech označovali jako normální, tedy každý z objektů obsahuje kolekci příslušejících objektů:

Vztah M:N

V metadatech se kolekce projeví stejným způsobem jako u normálního mapování 1:N, tedy pro každou kolekci je nutno pouze specifikovat třídu objektů v ní obsažených.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět