Hlavní navigace

JBoss: entity beans a dědičnost

Martin Večeřa

Dnes se podíváme na různé způsoby ukládání objektů, které jsou si navzájem potomky a rodiči, tedy mají mezi sebou nějakou hierarchii. Ukážeme si, jaké tabulky se při daném nastavení vytvoří v databázi, jak vypadají SQL dotazy pro načtení jednoho a všech objektů a jaké s tím jsou spojené výhody a nevýhody.

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í tabulkou
  • SINGLE_TABLE  – celá hierarchie objektů je uložena v jedné tabulce
  • TABLE_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.

Našli jste v článku chybu?

11. 1. 2009 20:00

Radek (neregistrovaný)
Právě jsem ten seriál celý prošel a připadá mi to docela dobré. Samozřejmě to nepokryje všechno, člověk, který je v JavaEE nový musí načíst ještě věci kolem, ale jako základ dobré. Nebo jste tam našel nějaké konkrétní bludy? Já bych jen uvítal, kdyby jednotlivé díly vycházely s kratšími přestávkami. S takovou za chvíli budete slavit rok od prvního dílu a to jich zas tolik není.

23. 12. 2008 23:43

YF (neregistrovaný)
serii techto clanku si nelze vykladat jinak nez jako pokus o zdiskreditovani enterprise javy :) tesim se na dalsi dil - je to zabavnejsi nez ty beckovy komiksy kazdou sobotu! :)
120na80.cz: 5 nejčastějších mýtů o kondomech

5 nejčastějších mýtů o kondomech

Vitalia.cz: Taky věříte na pravidlo 5 sekund?

Taky věříte na pravidlo 5 sekund?

DigiZone.cz: TV Philips a Android verze 6.0

TV Philips a Android verze 6.0

Podnikatel.cz: Změny v cestovních náhradách 2017

Změny v cestovních náhradách 2017

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá

Vitalia.cz: Když přijdete o oko, přijdete na rok o řidičák

Když přijdete o oko, přijdete na rok o řidičák

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

120na80.cz: Na ucho teplý, nebo studený obklad?

Na ucho teplý, nebo studený obklad?

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

Lupa.cz: Co se dá měřit přes Internet věcí

Co se dá měřit přes Internet věcí

DigiZone.cz: Recenze Westworld: zavraždit a...

Recenze Westworld: zavraždit a...

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Podnikatel.cz: Snížení DPH na 15 % se netýká všech

Snížení DPH na 15 % se netýká všech

Root.cz: Certifikáty zadarmo jsou horší než za peníze?

Certifikáty zadarmo jsou horší než za peníze?

Měšec.cz: mBank cenzuruje, zrušila mFórum

mBank cenzuruje, zrušila mFórum

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

Podnikatel.cz: Chaos u EET pokračuje. Jsou tu další návrhy

Chaos u EET pokračuje. Jsou tu další návrhy

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Vitalia.cz: Potvrzeno: Pobyt v lese je skvělý na imunitu

Potvrzeno: Pobyt v lese je skvělý na imunitu

DigiZone.cz: Rádio Šlágr má licenci pro digi vysílání

Rádio Šlágr má licenci pro digi vysílání