JBoss AS 5.0.0.GA
Jak již bylo uvedeno ve zprávičkách, vyšel aplikační server verze 5.0.0.GA (general availability, veřejně dostupný). I když stále má své mouchy, prošel testy a plně vyhovuje JavaEE5 standardu. Rád bych vás na tomto místě vyzval, abyste i vy hlásili nalezené chyby v systému evidence chyb.
Některé problémy jsou ale také vyřešeny. Například injekce business komponent do JSP stránky, kterou jsme se snažili používat.
<%! @EJB InheritanceGenerator generator; %>
Všimněte si vykřičníku v tagu <%!. Jak již víme, JSP stránka se překládá do servletu. Tento vykřičník pak říká, že následující kód bude umístěn ve výsledném servletu v části deklarace členských proměnných. Jedině tam může proběhnout injekce (nikoliv uvnitř metod při použití lokálních proměnných).
Dědičnost
Dědičnost si předvedeme na příkladu s dopravními prostředky. Mějme základní třídu Vehicle
, která definuje několik vlastností.
@Entity @Inheritance(strategy=JOINED) abstract public class Vehicle { @Id @GeneratedValue(strategy = AUTO) private Long id; private String manufacturer; private String type; private String model; private Integer wheels; private Integer doors; ... }
Zbytek těla třídy obsahuje jen get/set metody bez zvláštních anotací. Anotace @Inheritance
určuje, jakým způsobem budou do databáze ukládáni potomci daného objektu. Možnosti jsou:
JOINED
– nové vlastnosti potomků jsou ukládány do zvláštních tabulek (jedna pro jednoho potomka) a pro jeho načtení je provedeno spojení s hlavní tabulkouSINGLE_TABLE
– celá hierarchie objektů je uložena v jedné tabulceTABLE_PER_CLASS
– jedna kompletní tabulka na každou třídu
V případě použití SINGLE_TABLE
je potřeba vědět, která konkrétní třída je v záznamu uložena. To se provede vložením sloupce, kterému se říká diskriminátor. Standardně se tento sloupec jmenuje DTYPE. To je možné změnit pomocí anotace @DiscriminatorColumn
. Konkrétní hodnotu uloženou do tohoto sloupce je pak možné určit u potomků pomocí anotace @DiscriminatorValue
. Pokud tuto anotaci nepoužijeme, určí si server sám interní hodnotu. Situace hezky vystihuje příklad v dokumentaci.
Všimněte si, že třída Vehicle
je abstraktní, tudíž nelze vytvářet objekty přímo daného typu. Přesto se nám při použití typu JOINED
a SINGLE_TABLE
vytvoří tabulka VEHICLE
, kde se budou ukládat vlastnosti objektů. S typem JOINED
to budou společné vlastnosti, s typem SINGLE_TABLE
všechny vlastnosti.
V příkladu máme definovány potomky Car
a Bus
a navíc ještě potomka třídy Car
, kterým je třída SUV
.
@Entity public class Car extends Vehicle { private Integer valves; ... } @Entity public class Bus extends Vehicle { private Integer seats; ... } @Entity public class SUV extends Car { private Boolean is4x4; ... }
Dál je v příkladu použita ještě jedna bezestavová session bean InheritanceGeneratorBean
, která slouží pro vygenerování objektů, abychom mohli v databázi sledovat způsob uložení dat. Za povšimnutí stojí, že do JSP stránky injektujeme tuto business komponentu do proměnné, která je typu InheritanceGenerator
, což je pouze rozhraní, které session bean implementuje.
Nyní se podívejme na to, jak jsou data ve skutečnosti uložena v databázi při použití různých typů ukládání dědičnosti. Začnemě typem JOINED
.
JOINED |
|
VEHICLE | ID, DOORS, MANUFACTURER, MODEL, TYPE, WHEELS |
CAR | ID, VALVES |
SUV | ID, IS4X4 |
BUS | ID, SEATS |
Při použití hodnoty TABLE_PER_CLASS
není možné mít jeden automaticky generovaný klíč pro více tabulek. Řešením by bylo mít primární klíč zvlášť pro každého potomka. V přiloženém příkladu je to řešeno vypnutím automatického generování a nastavováním primárního klíče ručně. Je samozřejmě také možné manuálně pomocí vhodných anotací (viz předchozí díly) nastavit generování primárního klíče ze sekvence.
TABLE_PER_CLASS |
|
VEHICLE | žádná taková tabulka nebyla vytvořena |
CAR | ID, DOORS, MANUFACTURER, MODEL, TYPE, WHEELS, VALVES |
SUV | ID, DOORS, MANUFACTURER, MODEL, TYPE, WHEELS, VALVES, IS4X4 |
BUS | ID, DOORS, MANUFACTURER, MODEL, TYPE, WHEELS, SEATS |
Při použití typu SINGLE_TABLE
se vytvoří jedna tabulka se sloupečkem DTYPE
pro rozlišení typu uloženého objektu. Standardně se zde ukládá název třídy.
SINGLE_TABLE |
|
VEHICLE | ID, DTYPE, DOORS, MANUFACTURER, MODEL, TYPE, WHEELS, VALVES, IS4X4, SEATS |
Z výsledků je zřejmé, že nejvíce prostoru uspoříme při použití výchozí hodnoty JOINED
. Mohou však nastat situace, kdy se hodí i další dva způsoby uložení. Vyzkoušejme si nyní při různém nastavení získat SQL dotazem jeden objekt, všechny objekty daného typu a všechny objekty daného typu včetně potomků.
Začněme s nastavením typu dědičnosti na JOINED
a jednoduchým požadavkem – načtení všech objektů typu SUV
. Na to musíme použít poměrně komplikovaný dotaz, protože třída SUV je potomkem třídy Car a třída Car zase potomkem třídy Vehicle.
SELECT * FROM vehicle RIGHT JOIN car ON vehicle.id=car.id RIGHT JOIN suv ON car.id=suv.id;
V případě, že bychom se chtěli zaměřit na jeden objekt, stačí dotaz doplnit o podmínku WHERE
. Použijeme-li nastavení TABLE_PER_CLASS
nebo SINGLE_TABLE
, je dotaz o dost jednodušší.
SELECT * FROM suv -- pro TABLE_PER_CLASS SELECT * FROM vehicle WHERE dtype='SUV' -- pro SINGLE_TABLE
I zde je situace s doplněním podmínky WHERE
stejná. Vše se ale může změnit v případě, že bychom chtěli získat všechny objekty daného typu včetně potomků. Při dědičnosti typu JOINED
musíme použít několik dotazů a výsledek si sami spojit. Sjednocení (UNION) zde není možné použít, protože dotazy vrací rozdílný počet záznamů na řádek.
SELECT * FROM vehicle RIGHT JOIN car ON vehicle.id=car.id RIGHT JOIN suv ON car.id=suv.id SELECT * FROM vehicle RIGHT JOIN car ON vehicle.id=car.id SELECT * FROM vehicle RIGHT JOIN bus ON vehicle.id=bus.id
Stejný přístup musíme použít i v případě nastavení TABLE_PER_CLASS
, dotazy jen budou jednodušší.
SELECT * FROM suv SELECT * FROM car SELECT * FROM bus
Jednoduché řešení nabízí nastavení SINGLE_TABLE
.
SELECT * FROM vehicle;
Jak je vidět, má každé nastavení svoje výhody. Záleží jen na tom, čeho chcete dosáhnout. Jestli vám jde o maximální úsporu místa, nebo o práci s jednotlivými objekty odděleně, nebo o práci s více typy objektů zároveň…
Závěr
Na závěr mi už jen zbývá popřát vám pěkné Vánoce a štastný nový rok. Po dědičnosti přijde na řadu předposlední díl věnovaný business komponentám. Ten bude pojednávat o transakcích.