+ All Categories
Home > Documents > Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1...

Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1...

Date post: 04-Jun-2020
Category:
Upload: others
View: 11 times
Download: 0 times
Share this document with a friend
120
1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Huňka, CSc.
Transcript
Page 1: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

1

Distribuované programování s prvky komponent

XDPKO

Doc. Ing. František Huňka, CSc.

Page 2: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

2

Obsah 1 Distribuované systémy – základní architektura ................................................3 1.1 Distribuované systémy ............................................................................................3 1.2 Java EE, Enterprise JavaBeans – přehled ...............................................................6 1.3 Anotace - metadata v Javě a možnosti jejich využití ..............................................8 2 Technologie Enterprise JavaBeans ...................................................................12 2.1 Distribuované zpracování – základ EJB ...............................................................12 2.2 EJB služby middleware ........................................................................................13 2.3 Řízení zdrojů .........................................................................................................16 2.4 Primární služby .....................................................................................................20 3 Perzistence v Javě (Java Persistence) ................................................................22 3.1 EntityManager ......................................................................................................22 3.2 Perzistentní kontext ...............................................................................................23 3.3 Životní cyklus entitních beanů ..............................................................................25 3.4 Pakování perzistentní jednotky .............................................................................25 3.5 Získání EntityManager .........................................................................................27 4 Mapování perzistentních objektů ......................................................................35 4.1 Primární klíč – třídy a kompozitní klíče ...............................................................39 4.2 Mapování položek .................................................................................................41 5 Entitní relace .......................................................................................................44 5.1 Jednosměrná relace one-to-one .............................................................................44 5.2 Obousměrná relace one-to-one .............................................................................46 5.3 Jednosměrná relace one-to-many ..........................................................................47 5.4 Jednosměrná relace many-to-one ..........................................................................50 5.5 Obousměrná relace one-to-many ..........................................................................52 5.6 Obousměrná relace many-to-many .......................................................................53 5.7 Jednosměrná relace many-to-many ......................................................................55 5.8 Kaskádování ..........................................................................................................57 6 Dědičnost entit a zpětná volání posluchači .......................................................59 6.1 Jedna tabulka pro hierarchii tříd ...........................................................................59 6.2 Tabulka pro konkrétní třídu ..................................................................................61 6.3 Tabulka pro podtřídu ............................................................................................63 6.4 Zpětná volání entity a posluchači .........................................................................64 7 Dotazy a EJB QL ................................................................................................68 7.1 Quary API .............................................................................................................68 7.2 Stránkování výsledků ............................................................................................70 7.3 EJB QL .................................................................................................................71 8 Session beany .......................................................................................................78 8.1 Bezstavové session beany .....................................................................................78 8.2 Zpřístupnění vlastností prostředí ...........................................................................84 8.3 EJBContext ...........................................................................................................86 8.4 Životní cyklus bezstavových session beanů .........................................................88 9 Beany řízené zprávami .......................................................................................96 10 Transakce ..........................................................................................................109 10.1 Transakce řízené kontejnerem, beanem a klientem ............................................112 10.2 Transakční atributy .............................................................................................114

Page 3: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

3

1 Distribuované systémy, základní architektura

V této kapitole se dozvíte: • co jsou to distribuované systémy, • jaké jsou základní platformy pro tvorbu distribuovaných systémů • jaké je využití anotací (metadat v Javě.

Komponentová technologie Cíl – vytvořit softwarové součástky a ty používat jako např. hardwarové součástky. Základní pojmy: Rozhraní: • každá komponenta musí mít rozhraní, kterým komunikuje se svým okolím Sada rozhraní: • množina rozhraní, které komponenta zveřejňuje a poskytuje k použití aplikacím Identita komponenty: • umožňuje od sebe odlišit různé komponenty, je dána zpravidla jednoznačným

identifikátorem Anonymita klienta: • o svém prostředí (aplikaci, kde je použita) má komponenta vědět co nejméně; tím je její

použití univerzálnější Skládání součástek: • schopnost komponenty fungovat jako uživatel jiné komponenty Znovupoužití: • znovupoužití komponenty je třeba pečlivě plánovat Sdílení a napojení několika klientů: • sdílení komponent klientskými aplikacemi je velmi častým jevem 1.1 Distribuované systémy Distribuovaný systém bude mít intuitivně komponenty, které jsou distribuované na různých počítačích (jiných JVM). Na počítač, který hostí některé komponenty distribuovaného systému se odkazujeme jako na hostitelský (host). Koncept hostitelského počítače pak bude označovat všechny operační komponenty počítače, včetně hardware, síťového operačního systému a software. Distribuovaný systém má více než jednu komponentu na více než jednom hostitelském počítači. Tyto komponenty se vzájemně ovlivňují, jsou ve vzájemné interakci. Komponenty potřebují poskytovat vzájemný přístup ke službám a potřebují být schopny si požádat o služby navzájem. Teoreticky by komponenty mohly být propojeny navzájem pomocí primitivních operací síťového operačního systému. Prakticky by to bylo příliš složité pro mnoho aplikací. Middleware – mezivrstva, která pomáhá s heterogenitou a distribucí.

Page 4: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

4

Obr. 1.1 Hostitelský počítač a distribuovaný systém

Obr 1.2 Middleware v distribuovaném systému Úkoly middleware • Middleware přemosťuje mezeru mezi síťovým operačním systémem a komponentami. • Poskytuje navrhovateli vyšší úrovně abstrakce. • Implementuje vyšší úrovně abstrakce založené na základních operacích (primitives), které

poskytuje síťový operační systém. • Zapouzdřuje složitost před návrhářem. Typy middleware Vzdálené volání procedur (Remote Procedure Calls)

Hardware

Síťový operační systém

Hostitelský počítač

Komponenta 1

Komponenta N . . .

Hardware

Síťový operační systém

Hostitelský počítač

Komponenta 1

Komponenta N . . .

Middleware

Page 5: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

5

Vyvinuto počátkem 80. let firmou Sun Microsystem jako část platformy Open Network Computing. RPCs jsou operace, které mohou být vyvolány vzdáleně nad různými platformami hardware a operačních systémů. Systémy remote procedure call jsou základem objektově orientovaného middleware. Objektově orientované middleware Toto middleware se vyvinulo více méně z myšlenky RPC. Prvním z těchto systémů byl OMG produkt Common Object Request Broker Architecture CORBA. Microsoft přidal distribuované schopnosti svému Component Object Model (COM) a vytvořil .NET. Sun poskytl mechanismus pro Remote Method Invocation (RMI) v Javě. Vývoj objektově orientovaného middleware kopíroval vývoj objektově orientovaných jazyků, které se vyvinuly z procedurálních. Standardizace frameworku komponent První aplikační servery poskytovaly komponentní služby nestandardním, proprietálním (vlastnickým) způsobem. Nebyla dohoda, co je komponenta a jak bude komponenta poskytovat služby prostřednictvím aplikačního serveru. Výsledkem byla závislost na dodavateli aplikačního serveru. Standardizace frameworku komponent spočívá v: • Dohodě (kontraktu) na množině standardních rozhraní mezi aplikačními servery a

komponentami. • Tato dohoda umožní libovolné komponentě běžet na libovolném aplikačním serveru. • To dovolí, aby komponenty byly přepínány mezi aplikačními servery bez nutnosti měnit

kód, nebo potenciálně dokonce rekompilovat samotné komponenty. • Dodavatelé (vendors) aplikačních serverů, kteří implementují takový standardizovaný

framework komponent, zabezpečí svoji práci vyšší kvalitou implementace standardu, než pouze omezením (uzamčením) jejich zákazníků. „Standardní framework komponent.“

Tři různé architektury pro distribuované systémy • CORBA OMG • Platforma .NET - Microsoft • Enterprise JavaBeans Sun Microsystems CORBA • Common Object Request Broker Architecture, • OMG Object Managing Group, • ORB Object Request Broker klíčová komponenta poskytuje infrastrukturu umožňující

objektům vzájemnou komunikaci nezávisle na platformě či technice implementace objektů,

• IDL interface definition language. Platforma .NET • Technologie úzce svázána s platformou Microsoft • Původ v Microsoft Transaction Server (MTS), později přejmenován na COM+

(Component Object Model) • COM navržen na používání na desktopu, později upraven do služeb na straně serveru • Pro distribuovaný přístup se používá DCOM – distribuovaný objektový model komponent

– jádro dnešní technologie .NET

Page 6: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

6

1.2 Enterprise JavaBeans Je standardní komponentní architektura, sloužící k realizaci aplikační vrstvy informačního systému. EJB komponenty jsou objekty implementované vývojářem, které zajišťují vlastní aplikační (business) logiku systému. Komponenty EJB mají své uplatnění zejména ve tří-(a více) vrstvých distribuovaných aplikacích, kde se jedná o součásti platformy JavaEE.

Obr. 1.3 Typické vícevrstvé rozmístění Základní vlastnosti EJB • Poskytnout robustní infrastrukturu pro vývoj rozsáhlých škálovatelných informačních

systémů; • Umožnit vývojáři se soustředit výhradně na problémovou doménu; • Umožnit tvorbu znovupoužitelných komponent s využitím různých nástrojů od různých

dodavatelů a budování aplikací kombinováním takto vytvořených komponent; • Usnadnit distribuci a nasazování aplikací. • Většina standardních služeb (autentizace, autorizace, distribuovanost, transakce,

perzistence, řízení přístupu ke zdrojům , apod.) jsou zajišťovány kontejnerem a jejich konfigurace probíhá deklarativním způsobem.

• Klíčovou vlastností je interoperabilita, a to mezi jednotlivými kontejnery a aplikačními servery, tak i s jinými aplikacemi.

Portabilita – aplikace jsou vyvíjeny nezávisle na konkrétní platformě. Interoperabilita – běh aplikací je nezávislý na koncové platformě. Java EE Java EE – (Enterprise Edition) je standardním deštníkem pro prostředky počítání Java enterprise. V podstatě spolu sdružuje technologie pro kompletní vývoj enterprise-class na straně serveru platformy rozmístění v Javě. Java EE je významná, protože vytváří jednotnou platformu pro vývoj v Javě na straně serveru.

Database

Client

Presentation Tier

Business

Logic

Middleware Tier

Client

Presentation Tier

Client

Presentation Tier

Business

Logic

Middleware Tier

Page 7: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

7

K lepšímu porozumění skutečné hodnoty Java EE bude uveden část výčtu důležitých technologií a API, které Java EE implementace podporují. EJB (Enterprise JavaBeans) – definuje jak psát komponenty na straně serveru a poskytuje standardní smlouvu mezi komponentami a aplikačními servery, které je řídí. EJB je uhelným kamenem Java EE. Java API for Web Services (JAX-WS). Webové služby metadat pro platformu Java. Specifikuje různé anotace pro vývoj webových služeb a rozmístění. Je nově zavedený až od Javy EE 5.0. Javovské vzdálené volání metod (RMI) a RMI-IIOP. RMI je javovská nativní cesta ke komunikaci mezi distribuovanými objekty, kdy oba objekty běží v různých JVM. RMI-IIOP je rozšíření RMI, které může být použito na integraci platformy CORBA. Je to oficiální API, které je možné použít přímo v Javě EE. Java Naming and Directory Interfaces (JNDI) se používá k zpřístupnění jmen a adresářových systémů. Je to možné využít při propojování EJB komponent nebo jiných zdrojů přes počítačovou síť. Java Database Connectivity (JDBC) – je API pro zpřístupnění relačních databází. Java Transaction API (JTA) a Java Transaction Services (JTS) tyto prostředky dovolují komponentám využít spolehlivou transakční podporu EJB 3.0 Tato specifikace přináší výrazné zjednodušení vývoje (oproti EJB 2.1). Toho je dosaženo: • přechodem od komponent typu Entity EJB k POJO entitám (plain old java object), • nahrazením většiny deployment deskriptorů (popisovačů rozmístění) pomocí anotací

(komentáře s využitím metadat) • uplatnění techniky dependency injection Kontejner – základní činnost • řídí životní cyklus EJB komponent • zajišťuje autentizaci a autorizaci (JAAS) • umožňuje distribuci komponent (RMI-IIOP) • řídí transakce (JTA) • může zajišťovat perzistenci (JTA) • poskytuje přístup ke zdrojům (JDBC, JCX) • poskytuje další služby (JMS, JavaMail) • Nastavení atributů různých služeb (transakce, bezpečnost, přístup ke zdrojům … ) je

důsledně odděleno od kódu komponent a řeší se deklarativním způsobem (u verze 3.0 pomocí anotací).

• To zjednodušuje kód komponent a zároveň umožňuje jejich chování snadno přizpůsobit požadavkům při konkrétním nasazení, aniž by bylo nutné měnit jejich kód.

EJB komponenty • Entity EJB (přítomny kvůli zpětné kompatibilitě, nahrazuje je technologie Java

Persistence API, která je oddělena od specifikace EJB); • Stateless Session EJB • Stateful Session EJB • Message-Driven EJB Role při vývoji EJB aplikací • Vývojář komponenty • Sestavitel aplikace

Page 8: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

8

• Odborník na nasazení • Dodavatel EJB kontejneru • Dodavatel EJB serveru • Administrátor Distribuce a nasazení EJB komponent Komponenty spolu s popisovači nasazení (popis instalace) se zabalí do tzv. modulů, které je možné nasadit v prostředí libovolného kontejneru, splňujícího požadavky specifikace EJB. Při nasazení je ovšem nutné aplikaci nakonfigurovat, což se děje prostřednictvím úprav popisovačů nasazení.

Obr. 1.5 Spakování a rozmístění beanu Kdy použít EJB • Když musí být aplikace škálovatelná. • Když musí být podporován souběžný přístup a transakce. • Když bude existovat více různých typů klientů Kdy nepoužít EJB • Když to bude kanón na vrabce. • Když nejsou potřeba služby a vlastnosti poskytované touto technologií. • Vždy je však důležité logicky oddělit aplikační vrstvu od prezentace. Nevýhody EJB • Komponenty jsou závislé na kontejneru a jeho API. • Složitost a náročnost na osvojení (i když zredukováno). • Kontejner má vždy jistou režii. 1.3 Anotace – komentáře (metadata) Zásadní rozšíření jazyka Java 5.0. Anotace jsou značky vkládané do zdrojového kódu a určené většinou pro různé nástroje např. dokumentace programu, metadata pro EJB aplikace

JAR File Generator

Page 9: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

9

deployment descriptor (popis instalace). Umožní zapsání zdrojového kódu a poznámek (anotací) do jednoho souboru: • JavaBeans vyžadují třídu BeanInfo paralelně s beanem • EJB vyžadují popisovač instalace (deployment descriptor) dosud další soubor .xml Zpracování značek Značky mohou být zpracovány v následujících etapách (v jedné i ve všech třech): 1. mohou být určeny pouze pro nástroje pracující se zdrojovým kódem – čteny ze

zdrojového kódu 2. mohou být začleněny do přeloženého .class souboru a určeny pro nástroje pracující s

těmito soubory • nástroje připravující instalaci (rozmístění deployment) na aplikačním severu před

vlastním spuštěním • nástroje pro práci s knihovnami

3. mohou být začleněny do .class souboru a určeny pro zpracování za běhu programu

(runtime reflection). – Anotace patří mezi modifikátory. – Vkládá se před anotované položky (deklarace a definice) bez středníku. – Podle konvence se anotace vkládá před ostatní modifikátory. Zápis anotace • Anotace doplňuje javadoc značky (tags). • Od jiných syntaktických prvků odlišuje anotace znak ‘@’. Je to předpona.

– Znak ‘@’ používají i značky v dokumentačních komentářích. Anotace se nevyskytuje uvnitř komentářových závorek.

• Identifikátor, který za znakem následuje je identifikátor anotace. • Např. modifikátor @transient …- označuje datový atribut, který bude ignorován

serializací @deprecated … indikuje nepoužívat metodu

• Anotace nesmí deklarovat žádného předka – potomci rozhraní java.lang.anotation.Annotation nesmí se explicitně deklarovat – podobně jako enum

• Anotace bez parametrů, nedefinuje žádné parametry – značkovací anotace (marker annotations).

• Anotace s parametry.

// Deklarace anotace public @interface Testovat { } // Pouziti anotace public class MojeTrida { // . . . @Testovat // bezparametrická anotace, nemá závo rky public void chytraMetoda() { // . . . } }

Page 10: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

10

// indikace docasne specifikace - deklarace public @interface Preliminary { } // pouziti @Preliminary public class TimeTeravel { . . . }

Modifikatory @interface NazevAnotace { Typ NazevPrvku( ); // deklarace prvku anotacnih o typu // nebo Typ NazevPrvku( ) default [implicitni hodnota] ; } // deklarace public @interface ReguestForEnhancement { int id( ); String synopsis( ); String engineer( ) default “[unassigned]”; String date( ) default “[unimplemented]”; } // pouziti @ReguestForEnhancement ( id = 289745, synopsis = “Enable time-travel”, engineer = “Mr. Peabody”, date = “12/4/2008” ) public static void travelThroughTime(Date destin ation) { . . . }

Možnosti anotace • anotování:

– tříd – rozhraní – atributů – metod – parametrů metod

– balíčků – musí být definován soubor

package-info.java definice pouze v tom souboru, vlastní anotace před klíčovým slovem package

Anotace ve standardní knihovně 1. @Deprecated

– označená deklarace je považována za zavrženou a příslušný objekt/metoda by se neměly používat

2. @Override – označuje metody, které mají zastínit (překrýt) stejnojmenné metody rodičovské

třídy 3. @SuppressWarnings

– jako parametr má definován vektor textových řetězců specifikujících varování, která má překladač při překladu anotované entity potlačit.

4. @Documented – označená anotace musí být uvedena v dokumentaci. Program generující anotaci ji

pak bude uvádět jako součást popisu deklarace dané entity.

Page 11: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

11

5. @Target – specifikuje, které druhy entit je možné jí označít. Má jeden parametr výčtového

typu: java.lang.anotation.ElementType • ANNOTATION_TYPE Definice jiné anotace • CONSTRUCTOR Definice konstruktoru • FIELD Deklarace atributu • LOCAL_VARIABLE Deklarace lokální proměnné • METHOD Deklarace metody • PACKAGE Deklarace balíčku • PAREMETER Deklarace parametru • TYPE Deklarace třídy, rozhraní, výčtového typu.

• Neoznačená definice anotace @Target znamená, možnost deklarace libovolného z

uvedených typů. 6. @Inherited

• definice s ní označená se stane součástí dědictví dceřinných tříd. Při dotazu na anotaci @Inherited, pak v případě záporné odpovědi bude automaticky dotazována její rodičovská třída a eventuálně její rodičovská třída atd.

7. @Retention • definuje dobu uchování informací předaných anotací. Jednoparametrická anotace s

parametrem výčtového typu java.lang.anotation.RetentionPolicy Anotace ve standardní knihovně Retention • SOURCE

anotace určená pouze pro programy pracující se zdrojovým kódem – soubory .java • CLASS

anotace určená pro programy pracující s .class soubory např. programy pro instalaci (deployment) aplikací a knihoven. Class loader tento typ anotace nezpracuje a do class-objektu dané třídy ji nezačlení.

• RUNTIME informace daného typu anotace se dostanou až do class-objektu dané třídy, které anotace označuje. Prostřednictvím mechanismu reflexe proto bude možné za běhu programu příslušné informace uvedené v anotaci zjistit a podle nich reagovat.

Reflexe Reflexe je proces, kterým počítačový program může pozorovat a modifikovat svoji vlastní strukturu a chování. Reflexe v Javě dovoluje javovskému kódu vyhledávat informace o datových atributech, metodách a konstruktorech třídy nahrané do operační paměti počítače.

Úvodní kapitola popisuje a vysvětluje základní pojmy nutné k dalšímu studiu. Jsou zde popsány základní distribuované platformy. Především se ale úvodní kapitola věnuje technologii EJB a její verzi 3. Dále kapitola obsahuje popis a vysvětlení anotací (metadat), které se objevily v Javě od verze 5. Na praktických příkladech je vysvětleno jejich použití.

Page 12: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

12

2 Technologie Enterprise JavaBeans

V této kapitole se dozvíte: • co tvoří základ EJB, • jaké základní služby EJB poskytuje, • co je to řízení zdrojů, • jaké jsou základní primární služby.

Fyzicky představuje EJB dvě věci v jedné: • Specifikace (může být členěna do dokumentů) navrhuje pravidle úmluvy mezi aplikačním

serverem a komponentami. • Množina javovských rozhraní. Komponenty a aplikační servery musí vyhovovat těmto

rozhraním. Protože jsou všechny komponenty psány podle stejných rozhraní, pro aplikační server vypadají všechny stejně. Z toho důvodu může aplikační server řídit libovolnou komponentu.

EJB jako aplikační vrstva komponent Skutečný rozdíl mezi prezentační vrstvou komponent a enterprise beany je v doméně, ve které pracují. Prezentační vrstva zpracovává operace na straně klienta. Enterprise beany zpracovávají komponenty na straně serveru. 2.1 Distribuované zpracování - základ EJB EJB umožňuje vývoj a instalaci (deployment) distribuovaných komponent – distribuovaných objektů (vzdálených objektů). Vzdálené volání metody distribuovaného objektu sleduje běžný postup, který je podobný téměř u všech distribuovaných technologií. Hlavní kroky postupu: 1. Klient volá stub – proxy objekt na straně klienta. Stub utajuje síťovou komunikaci před

klientem. Stub prostřednictvím soketů komunikuje s počítačovou sítí, ví jak zaslat parametry javovských reprezentací reprezentacím v síti.

2. Stub volá počítačovou sítí skeleton, což je proxy objekt na straně aplikačního serveru. Skeleton utajuje síťovou komunikaci od distribuovaných objektů. Skeleton umí přijmout volání prostřednictvím soketů, stejně tak převést parametry z jejich síťové reprezentace do javovské reprezentace.

3. Skeleton deleguje volání na vhodnou implementaci distribuovaného objektu. Tento objekt obslouží volání (klienta), provede svoji práci a vrátí řízení skeletonu. Ten následně vrací řízení stubu, který konečně vrací řízení vzdálenému klientovi.

Klíčovým bodem je, že jak stub, tak i implementace objektu na straně serveru implementují stejné rozhraní – remote interface. To znamená, že stub klonuje hlavičky (signatures) metod distribuovaného objektu. Klient, který volá metodu stubu si myslí, že volá distribuovaný objekt přímo; ve skutečnosti klient volá prázdný stub, který ví, jak se dostat přes počítačovou síť ke skutečné implementaci (distribuovanému objektu). Tomu se říká distribuovaná transparentnost. Ve skutečnosti je distribuovaný objekt abstrakce, která je vytvořena spoluprací mezi stubem a skeletonem a objekty implementace. Žádná samostatná entita není v tomto scénáři distribuovaný objekt.

Page 13: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

13

Obr. 2.1 Distribuované zpracování 2.2 EJB služby middleware Služby middleware může EJB poskytovat explicitně a implicitně. Explicitní přístup vyžaduje explicitně volat API služby middleware. Implicitní přístup je bez nutnosti psaní API služeb middleware. Explicitní p řístup ke službám middleware Kód je používá middleware API, aby požádal framework k provedení požadovaných služeb. Následující příklad ukazuje pseudokód metody transfer distribuované komponenty Bank, která provádí (přesun) částek mezi účty: transfer(Account account1, Account account2, long amount) { // 1: Volá API middleware k provedení bezpečnostní kontroly // 2: Volá API middleware pro spuštění transakce // 3: Volá API middleware k naplnění řádků z databáze // 4: Odečte částku od jednoho účtu a přidá ji k druhému účtu // 5: Volá API middleware pro uložení řádků do databáze // 6: Volá API middleware k ukončení transakce } Ačkoli jsme obslouženi s předepsaným middleware prostřednictvím frameworku, naše aplikační logika je protkána s logikou volání toto API middleware. Tento postup má jisté stinné stránky: • Snížená produktivita vývojáře. I přes to, že framework poskytuje služby middleware,

stále se předpokládá, že vývojář bude psát kód který je bude využívat. Psaní a testování kódu vždy zabírá čas a takto snižuje produktivitu.

• Obtížnost psaní. Kód je „nadutý“. Jednoduše chceme provádět transfer, ale to vyžaduje velké množství kódu z důvodu smísení kódu interakcí služeb s kódem aplikační logiky.

• Obtížná údržba. V případě, že chcete změnit způsob jakým využíváte služby middleware, musíte přepsat váš kód.

Page 14: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

14

Implicitní p řístup k middleware S tímto přístupem framework nejen poskytuje služby middleware, ale také jednodušší způsob jejich použití. Implicitní framework middleware dovolí deklarovat služby middleware, které potřebujete pro vaši aplikaci v odděleném popisovači souboru, nebo dokonce prostřednictvím jednoduchých anotací, uvnitř vlastního kódu. Takto váš kód již neobsahuje žádné těžkopádné volání API pro použití služeb middleware. Kód je jasný se zaměřením na aplikační (business) logiku. Následuje předchozí příklad přepsaný za použití implicitního přístupu k middleware: transfer(Account account1, Account account2, long a mount) { // 1: Ode čte částku z jednoho ú čtu a p ři čte ji k jinému ú čtu }

V době kompilace předchozího kódu, si framework prohlédne (prozkoumá) popisovač (deskriptor) a / nebo anotaci uvnitř kódu a provede požadované služby middleware. Takto je mechanismus použitý na poskytování služeb middleware implicitně je implementačním detailem serveru EJB a je ponecháno na prodejcích (dodavatelích), aby se rozhodli individuálně. Výhody tohoto řešení jsou následující: • Zvýšení produktivity vývojáře. Vývojáři nemusí psát kód pro vyvolání služeb

middleware. Vše co musí udělat je, deklarovat služby, které vyžadují v popisujícím souboru s využitím XML nebo anotací v samotném kódu.

• Snadný zápis. Protože není třeba psát žádný kód k vyvolání služeb middleware, váš kód komponenty může být zaměřen na aplikační logiku.

• Snadná údržba. Oddělení business (aplikační) logiky od logiky middleware je snadno udržovatelé. Změna služeb middleware nevyžaduje změnu kódu aplikace.

Implicitní &explicitní využívání služeb middleware EJB používá implicitní přístup k middleware, avšak poskytuje také jednoduché API ke konkrétním interakcím se službami middleware. Ačkoli přístup přes API je složitější, nechává větší řízení v rukách vývojáře. Např. v případě kdy vývojář nechce označit celou metodu v EJB jako transakční. V tom případě musí použít javovské transakční API k interakci s transakčním řízením služeb EJB. Použitím API služeb middleware může vývojář označit začátek a konec transakce v konkrétních bodech uvnitř kódu metody a tímto vykonávat lepší řízení. EJB poskytuje obě možnosti využívání služeb middleware. 2.3 Řízení zdrojů Servery EJB také řídí zdroje, které používají beany a mohou dále řídit mnoho distribuovaných objektů. Musí řídit a rozhodovat, jak budou distribuobané objekty používat paměť, vlákna, databázová připojení atd. Servery EJB podporují tyto služby: • konkurentnost • řízení transakcí • perzistenci • distribuci objektů • pojmenování (naming)

Page 15: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

15

• bezpečnost Služby představují jistý druh infrastruktury potřebný pro fungování třívrstvé architektury. Komponenty na straně serveru Enterprise beany v Javě, zapouzdřují business (aplikační) logiku = kód který požaduje aplikace • session beany – vykonávají úlohy klienta • beany řízené zprávami – fungují jako posluchači pro konkrétní typ asynchronní zprávy Obsah enterprise beanu – pro jeho vytvoření jsou potřebné následující soubory: • Třída enterprise bean – implementuje metody definované v aplikačním rozhraní a jakékoli

metody životního cyklu zpětného volání (callback). • Business rozhraní – lokální, vzdálené rozhraní, definuje metody implementované třídou

enterprise bean. • Helper classes – třídy výjimek, pomocné třídy pro třídu enterprise bean. Uvedené soubory se spakují do EJB JAR modulu, ve kterém je uložen enterprise bean.

//Vzdálené rozhraní bezstavového session beanu @javax,ejb.Remote; @Remote public interface CalculatorRemote { public int add(int x, int y); public int subtract(int x, int y); }

//T řída stateless session bean import javax.ejb.*; @Stateless public class CalculatorBean implements CalcularRem ote { public int add(intx, int y) { return x+y ; } public int subtract(int x, int y) { return x-y ; } }

Entitní beany Entitní bean – je lehký (lightweight) perzistentní doménový objekt tzv. POJO. • Typická entita reprezentuje tabulku v relační databázi. • Instance entitního beanu koresponduje se řádkem této tabulky. • Ke každé entitě musí existovat entitní třída Požadavky na entitní třídu

Page 16: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

16

• Třída musí být opatřena komentářem (anotovaná) @javax.persistence.Entity • Modifikátor třídy musí být public nebo protected, musí obsahovat neparametrický

konstruktor, může obsahovat další konstruktory • Třída nesmí mít modifikátor final. Žádná metoda, stejně jako perzistentní instanční

proměnné (datové atributy) nesmí být deklarovány final. • Pokud je instance entity předávaná hodnotou jako odpojený (detached) objekt

prostřednictvím rozhraní session beanu, třída musí implementovat rozhraní Serializable. • Entitní třídy mohou být podtřídami jiných entitních, nebo neentitních tříd, neentitní třídy

mohou mít pod sebou entitní třídy. • Perzistentní instanční proměnné (datové atributy) entitní třídy mohou být deklarované

jako private nebo protected a zpřístupňovány pomocí přístupových a modifikačních metod (get/set).

package com.titan.domain; import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.Column; import javax.persistence.Id; @Entity @Table(name="CABIN") public class Cabin implements java.io.Serializable { private int id; private String name; private int deckLevel; @Id @Column(name="CABIN_ID") public int getId() { return id; } public void setId(int pk) {id = pk; } @Column(name="CABIN_NAME") public String getName() { return name; } public void setName(String str) { name = str; } @Column(name="CABIN_DECK_LEVEL") public int getDeckLevel() { return deckLevel; } public void setDeckLevel(int level) { deckLevel = level; } }

2.3 Řízení zdrojů Velký aplikační systém s mnoha uživateli vyžaduje tisíce až miliony objektů, které se využívají současně. Protože počet interakcí mezi objekty roste, roste i časová odezva systému a frustrace uživatelů. Servery EJB zvyšují výkonnost synchronizací interakcí mezi objekty a sdílením zdrojů. Mezi počtem klientů a počtem distribuovaných objektů, které jsou třeba k obsloužená klientů je přímá úměra.

Page 17: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

17

EJB explicitně podporuje dva mechanismy zvyšující výkonnost: 1. instance pooling (sdílení instancí) pro bezstavové session beany (stateless) 2. aktivační mechanismus – pro stavové session beany (stateful)

Instance looping (sdílení instancí) Koncept sdílení zdrojů se využíval již dříve např. pool databáze connection – business objekty v systému mohou sdílet přístup do databáze. Tento nápad snižuje počet databázových připojení a zvyšuje propustnost systému. Mnoho EJB kontejnerů používá také sdílení zdrojů k serverové straně komponent; tato technika se nazývá instance pooling. Klienti session beanů jsou v interakci s těmito beany prostřednictvím vzdáleného nebo lokálního rozhraní, které je implementované EJB objekty. Klientské aplikace nikdy nemají přímý přístup k instanci třídy session beanu. Instance pooling (sdílení instancí) je možné, protože klienti nemají k beanům přímý přístup. Proto neexistuje žádný pádný důvod, udržovat oddělené kopie každého beanu pro každého klienta. Server proto může udržovat mnohem menší množství beanů a znovu využít každou instanci beanu k obsluze jiného požadavku. Životní cyklus entitních beanů • Žádný stav (no state)

– bean ještě nevytvořil instanci; stav pouze pro popis začátku životního cyklu beanu • Sdílený stav (pooled state)

– kontejner již vytvořil instanci, ale ta ještě nebyla asociována s objektem EJB • Připravený stav (ready state)

– instance byla asociována s objektem EJB a je připravena reagovat na zavolání její metody

Sdílení instancí Protože bezstavové session beany neudržují žádný stav mezi vyvoláváním metod, každé volání metody pracuje nezávisle tak, že vykonává operaci bez využití instančních proměnných (datových atributů třídy). To znamená, že libovolná instance bezstavového session beanu může obsloužit požadavek libovolného EJB objektu vhodného typu. Kontejner proto může „přepínat“ instance beanu mezi voláním metod klientů. Každý dodavatel EJB implementuje sdílení instancí odlišně, ale všechny strategie sdílení instancí se snaží, aby byly instance rychle dosažitelné za běhu. Když klienti vytvoří požadavky na business metody, instance beanů ze sdílené oblasti jsou přidělovány EJB požadavkům a asociovány s klienty. Po vykonání požadavku, EJB objekt již dále nepotřebuje instanci beanu a ta je vrácena do společné oblasti. EJB server udržuje sdílenou oblast pro všechny typy rozmístěných bezstavových session beanů.

Page 18: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

18

Obr. 2 .2 Bezstavové session beany a strategie swapping (prohazování )

Obr. 2.3 Bezstavové session beany a strategie swapping (prohazování ) EJBContext Brzy poté, co je instance umístěna do společné oblasti, je odkaz na ni dán do javax.ejb.Context. Tento context objekt je injektovaný v případě požadavku na instanci session beanu. EJBContext poskytuje rozhraní, kterým bean může komunikovat s EJB prostředím. EJBContext je velmi užitečný, když se instance beanu se přemisťuje do stavu ready (připravený stav). Když instance beanu obsluhuje požadavek, EJBContext získává nový význam. Poskytuje informace o klientovi, který používá danou instanci beanu. Instanci dále poskytuje přístup k její vlastní EJB stub proxy, což je užitečné, když bean potřebuje předat odkaz na sebe, nebo na jiný enterprise bean. EJBContext tedy není statická třída, ale je to rozhraní ke kontejneru.

1

2

A

B

C

D

Instance Pool stub

stub

EJB object

EJB server

bean instances

Client Applications

1

2 A

C

D

Instance Pool stub

stub

EJB object

EJB server

bean instances

Client Applications

B

B

Page 19: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

19

Beany řízené zprávami a instance pooling Beany řízené zprávami rovněž neudržují žádné stavové veličiny a proto jsou rovněž vhodné pro sdílení instancí. Ve většině EJB kontejnerů, má každý typ beanů řízených zprávami svoji vlastní oblast sdílených instancí, které obsluhují příchozí zprávy. Aktiva ční mechanismus Stavový session bean udržuje stav mezi voláními metod. Konverzační stav reprezentuje pokračující konverzaci mezi stavovým session beanem a klientem. Stavové session beany na rozdíl od bezestavových, entitních a beanů řízených zprávami nemají mechanismus instance pooling. Místo toho používají aktivační mechanismus, který zachovává (konzervuje) zdroje. Když EJB server potřebuje zachovat (konzervovat) zdroje, přesune stavový session bean z operační paměti (bean je serializovaný a přemístěný do druhotné paměti). Když klient vyvolá metodu EJB objektu, je vytvořena nová instance stavového beanu se stavem odpovídajícímu inicializovanému beanu. Pasivace (passivation) je činnost deasociace instance stavového beanu od EJB objektu a uložení jeho stavu. Aktivace stavového session beanu je znovu obnovení instance stavového beanu relativně k jeho EJB objektu. Když přijde požadavek na metodu pasivovaného beanu, kontejner automaticky vytvoří novou instanci a nastaví atributy na hodnoty uložené během pasivace.

Obr. 2.4 Proces aktivace a pasivace beanu Aktivační proces je podporovaný metodami zpětného volání životního cyklu. Anotační metoda @javax.ejb.PostActivate je vyvolaná ihned následně po úspěšné aktivaci instance beanu, je-li samozřejmě deklarovaná ve třídě beanu. Anotační metoda javax.ejb.PrePassivate

EJB object B

EJB server

secondary storage

bean instance

EJB object C

EJB server

secondary storage

bean instance

EJB object

EJB server

secondary storage

bean instance eviction

B

stub

Client

stub

Client

stub

Client

Page 20: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

20

je vyvolaná bezprostředně před pasivací instance beanu opět, je-li vývojářem definovaná ve třídě beanu. Tyto dvě metody jsou zvláště užitečné při aktivaci a pasivaci stavového beanu. Protože instance stavového beanu je vypovězena z paměti, otevřená spojení se zdroji nejsou udržována. Výjimky jsou vzdálené odkazy na jiné beany a SessionContext, který musí být udržovaný se serializovaným stavem beanu a rekonstruovaný, když je opět bean aktivovaný. EJB také vyžaduje, aby během pasivace byly udržovány odkazy na prostředí JINI kontextu, rozhraní komponent a služba EntityManager a objekt UserTransaction. 2.4 Primární služby • Konkurentnost (souběžnost), řízení transakcí, perzistence, distribuované objekty,

asynchronní posílání zpráv, timer, naming (služba jmen), bezpečnost • Souběžnost (concurrency) – je důležitá pro všechny typy beanů, ale liší se podle typu

beanů. Konkurentnost pro session a entitní beany Stavové a bezstavové session beany nepodporují souběžnost. Vždy slouží pouze klientu, který je vytvořil a nesdílí žádní data. Entitní beany reprezentují data, která mohou být sdílena a zpřístupněna souběžně. V distribuovaném systému objektů vzniká problém, když je snaha sdílet distribuované objekty mezi klienty. Dva klienti používají současně jeden EJB objekt?? jeden čte a druhý dělá změny?? Řešení: Několik klientů může být napojeno na jeden EJB objekt, ale pouze jeden klient (vlákno) může mít přistup k instanci beanu v daném čase. Ostatní klienti čekají, až se dokončí vyvolaná metoda, resp. celá transakce. Kompletní souběžnost řídí EJB kontejner. Proto také beany nesmí vytvářet své vlastní vlákna. Beany řízné zprávami jsou sdílitelné (poolable) a použitelné pro zpracování příchozích zpráv souběžně. Služba pojmenování Poskytují klientům mechanismus pro nalezení distribuovaných objektů a služeb. Služba pojmenování: • vazbu s objektem (object binding) • vyhledání API (lookup API) Object binding je asociace distribuovaného objektu se jménem, nebo identifikátorem přirozeného jazyka. Např. objekt TravelAgentRemove může být svázán se jménem „TravelAgentRemote“ nebo se jménem „agent“. Vazba je jednoduše ukazatel, nebo index na distribuovaný objekt. Vyhledání API umožňuje klientovi připojit se na distribuovanou službu a požádat o vzdálený odkaz ke specifikovanému objektu. Enterprise JaveBeans výhradně používá JNDI jako API pro vyhledávání pro javovské klienty. JNDI podporuje libovolný druh jmenné a adresářové služby.

Page 21: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

21

Javovské klientské aplikace mohou užívat JNDI k inicializaci spojení se serverem EJB a k lokalizaci EJB specifik. javax.naming.Context jndiContext = new javax.naming .InitialContext(); Object ref = jndiContext.lookup(“TravelAgentRemote“ ); TravelAgentRemote agent = (TravelAgentRemote) PortableRemoteObject.narrow(ref, Tra velAgentRemote.class); Reservation res = agent.bookPassage( . . .);

Objekt jndiContext má informace o EJB serveru a o tom, jaký JNDI driver je třeba nahrát. Metoda Context.lookup() říká poskytovateli JNDI služby jméno objektu, které se má vrátit z EJB serveru. Jakmile je vzdálené rozhraní k dispozici TravelAgent EJB, je možné začít volat metody k provádění požadovaných služeb.

Tato kapitola se již detailněji zabývá technologií Enterprise JavaBean. Popisuje základní služby middleware EJB a jejich použití. Dále se věnuje řízením zdrojů a primárním službám.

Page 22: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

22

3 Perzistence v Javě (Java Persistence)

V této kapitole se dozvíte: • jaká je základní filosofie využití perzistence v technologii EJB • jaké jsou základní funkce Entitního manažera (EntityManager) • k čemu se využívají perzistentní jednotky.

Java Persistence je oddělený dokument specifikací od jádra specifikace EJB. Je to nejdůležitější inovace verze EJB 3.0. Specifikace Java Persistence: • poskytuje standardní objektově relační mapování (integruje mnoho konceptů z Hibernate

a JDO) • není pouze svázaná s kontejnerem Java EE a může být testována a použita také v

prostředích J2SE • definuje rozhraní service provider, takže různí persistence providers mohou být použiti

bez vlivu na kód entity Perzistence je klíčová část platformy Java EE. EntityManager je ústřední služba pro všechny perzistentní akce – přenos entitních informací do/z relační databáze. Entity jsou obyčejné javovské objekty, které jsou stejně alokovány. Nejsou perzistentní, dokud tak neučiní EntityManager. 3.1 EntityManager EntitníManager řídí objektově relační mapování. API EntityManager poskytuje metody pro tři odlišné druhy operací:

• metody řízení životního cyklu entit • metody synchronizace databázových operací • metody pro vyhledání a dotazy na entity

Dále poskytuje cachovací a transakční služby. Entity & session beany Entity mají klientem viditelnou perzistentní identitu (primární klíč), který se liší od jejich objektové reference. Entity mají perzistentní, klientem viditelné stavy. Entity nejsou vzdáleně přístupné (remotely accessible). Život entity může být kompletně nezávislý na životě aplikace (lifetime). Příkladem je entita Customer. Customer cust = new Customer(); cust.setName(“Bill”);

Entita zůstane obyčejným objektem, dokud nepožádáme entitního mamažéra o její uložení do databáze.

import javax.persistence.*;

Page 23: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

23

import java.util.Date; @Entity @Table(name="CUSTOMER_TABLE") public class Customer implements java.io.Serializab le { private int id; private String lastName; private String firstName; private CustomerType customerType; private Date timeCreated = new Date(); private JPEG picture; @Id @GeneratedValue @Column(name="CUST_ID") public int getId() { return id; } public void setId(int pk) { id = pk; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this. lastName = lastName; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @Enumerated(EnumType.STRING) public CustomerType getCustomerType() { return c ustomerType; } public void setCustomerType(CustomerType type) { customerType = type; } @Temporal(TemporalType.TIME) public Date getTimeCreated() { return timeCreate d; } public void setTimeCreated(Date time) { timeCrea ted = time; } @Lob @Basic(fetch=FetchType.LAZY) public JPEG getPicture() { return picture; } public void setPicture(JPEG jpeg) { picture = jp eg; } }

3.2 Perzistentní kontext Protože entity nemohou být zpřístupněny vzdáleně, je proto možné je rozmístit pouze lokálně a použít je buď z J2SE kódu mimo kontejner EJB, nebo z session beanů nebo zprávami řízených beanů z kontejneru EJB. Perzistentní kontext je spojení mezi vaší instancí v paměti a databází. Perzistentní kontext je manipulovatelný prostřednictvím API EntityManageru. Perzistentní kontext je množina řízených (managed) entitních instancí. EntityManager sleduje všechny entitní objekty uvnitř perzistentního kontextu kvůli změnám, aktualizacím v databázi s využitím pravidel flush modu. Poté co je perzistentní kontext uzavřený, všechny připojené entity (řízené) se stanou odpojenými a nejsou tedy dále řízeny EntityManagerem. Stav odpojených entitních objektů není synchronizován s databází. Existují dva typy perzistentních kontextů:

Page 24: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

24

1. perzistentní kontext ohraničený transakcí Transaction-scoped persistence context

2. rozšířený perzistentní kontext Extended persistence context

Perzistentní kontext ohraničený transakcí Tento kontext žije stejně dlouho jako transakce a končí s jejím dokončením. Na konci je tento kontext zrušen a instance entit se stanou odpojenými. Pouze perzistentní kontexty řízené aplikačním serverem mohou být transakčně ohraničené. Jinými slovy pouze instance EntityManageru injektované prostřednictvím anotace @PersistenceContext nebo jejího XML ekvivalentu, mohou být transakčně ohraničené (omezené). @PersistenceContext(unitName=”titan”) EntityManager entityManager; @TransactionAttribute(REQUIRED) public Customer someMethod() { Customer cust = entityManager.find(Customer.cla ss, 1); cust.setName(“new name”); return cust; }

Když má být prováděná metoda someMethod(), kontejner EJB ji vyvolá právě z kontextu JTA transakce (Java Transaction API). Odkaz na instanci entitního beanu Customer je získán z EntityManagera a metoda setName() změní jméno entity Customer. Instance beanu Customer bude řízena po celou dobu transakce. To znamená, že např. změny jména budou synchronizovány s databází. Na konci metody je vrácen odkaz na instanci Customer. Po ukončení transakce je transakčně ohraničený kontext zničen a instance entity Customer již není dále řízena. Vyvolání metody setName() po ukončení transakce jinak neovlivní obsah databáze. Používá se u bezstavových session beanů. Např. metody openAccount(), deposit(), withdraw(), getBalance() vždy vytváří nový perzistentní kontext. Rozšířený perzistentní kontext má delší životnost než transakce, využívá se u stavových session beanů. Instance entit, které jsou připojeny k rozšířenému perzistentnímu kontextu, jsou stále řízeny i po skončení transakce. Toto je zvláště užitečné v případech, kdy je třeba „konverzovat“ delší dobu s databází, ale není žádoucí mít transakce běžící dlouhou dobu, protože transakce představují hodnotné zdroje jako třeba JDBC spojení a zámky databáze. Viz následující příklad: Customer cust = null; transaction.begin(); // za čátek transakce 1 cust = extendedEntityManager.find(Customer.class , 1); transaction.commit(); // konec transakce 1 transaction.begin(); // za čátek transakce 2 cust.setName(”Bill”); extendedEntityManager.flush(); // uložení zm ěn do databáze transaction.commit(); // instance cust z ůstává p řipojena, zm ěny jsou // uloženy do DB

V příkladu je lokální proměnná cust inicializována voláním metody find() a je stále řízena i po skončení transakce 1.

Page 25: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

25

3.3 Životní cyklus entitních beanů • new. Entitní instance byla vytvořena v paměti, atributy jsou naplňovány daty, ale není

ještě asociovaná s perzistentní identitou v databázi nebo s perzistentním kontextem. (Změny stavů atributů entitní instance nejsou synchronizovány s databází).

• managed. Entita má perzistentní identitu v databázi a je aktuálně asociovaná s perzistentním kontextem. Je to stav po vyvolání metody persist().

• detached (odpojena). Entita má perzistentní identitu, ale není nebo již dále není asociovaná s perzistentním kontextem.

• removed. Entita je aktuálně asociovaná s perzistentním kontextem, ale je naplánovaná pro odstranění z databáze.

Obr. 3.1 Životní cyklus entitních beanů Odpojené entity (Detached Entity) Zajímavá stránka odpojených entit je v tom, že mohou být serializované a poslané sítí vzdálenému klientu. Klient může nyní provést změny v instanci entity, a poslat instanci entity zpět serveru, aby mohla být opět synchronizována s databází (změny datových atributů jsou uloženy do databáze). 3.4 Pakování a instalace (rozmístění) entitních tříd Entitní třídy jsou pakovány a instalovány v perzistentních jednotkách (persistence unit) . Perzistentní jednotka je logické seskupení entitních tříd, metadat mapování a konfiguračních dat pro databázi. Perzistentní jednotka je definovaná v souboru persistence.xml. Je to deployment deskriptor vyžadovaný specifikací Java Persistence. Soubor persistence.xml definuje jednu nebo více perzistentních jednotek a je uložen v adresáři META-INF. Existují následující typy: • obyčejný soubor JAR uvnitř classpath regulárního programu Java SE,

Page 26: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

26

• soubor EJB-JAR. Perzistentní jednotka může být včleněna do rozmístění EJB. • soubor JAR v adresáři WEB-INF/lib ve tvaru .war (web archive) • soubor JAR v kořenu enterprise archive (.ear) • soubor JAR v adresáři EAR lib. Popisovač rozmístění persistence.xml definuje identitu a konfigurační vlastnosti pro každou perzistentní jednotku. Každá perzistentní jednotka musí mít identitu, i prázdný řetězec je platné jméno. Nejjednodušší příklad: <?xml version=“1.0” encoding=“UTF-8”?> <persistence xmlns=http://java.sun.com/xml/ns/persi stence> <persistence-unit name=“intro”/> </persistence>

Každá persistentní jednotka je svázána pouze s jedním datovým zdrojem. V prostředí Java EE je tato asociace definovaná specifickým elementem XML. Kořen schéma XML persistence.xml je element <persistence>, který obsahuje jeden, nebo více elementů <persistence-unit> . Viz následující příklad. <persistence> <persistence-unit name="titan"> <jta-data-source>java:/OracleDS</jta-data-sou rce> <properties> <property name="org.hibernate.hbm2ddl">upd ate</property> </properties> </persistence-unit> </persistence>

Atribut name definuje jméno, podle kterého se na jednotku odkazujeme. Tento atribut je nutný a je používán injektovanými anotacemi a XML popisovači rozmístění. Atribut transaction-type, je volitelným atributem elementu <persistence-unit> a definuje, zda chceme, aby perzistentní jednotka byla řízena a integrována Java EE transakcemi (JTA), nebo používala lokální zdroj (RESOURCE_LOCAL) API javax.persistenceEntityTransaction k řízení integrity vašich instancí EntityManagerem Element <persistence-unit> má následující pod-elementy: • <description> volitelný komentář popisující danou perzistentní jednotku • <provider> volitelný element je plně kvalifikované jméno třídy nebo implementací

rozhraní javax.persistence.PersistenceProvider. Většinou nedefinujeme. • <JTA-data-source> volitelný definujete, pokud používáte: JTA nebo

RESOURCE_LOCAL perzistentní jednotky. Tyto elementy určují specifickou identitu dodavatele konkrétního datového zdroje. Obyčejně je tento řetězec globálním jménem JNDI pro odkaz na datový zdroj.

• <maping-file> volitelný • <jar-file> volitelný • <class> volitelný • <properties> volitelný element definuje množinu atributů specifikovaných dodavatelem,

předaných správci persistence. • <exclude-unlisted-classes> volitelný

Page 27: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

27

Perzistentní jednotka Pokud nejsou specifikována žádná jiná metadata uvnitř souboru persistence.xml, soubor JAR, který obsahuje persistence.xml, bude skenován od kořene po libovolnou třídu anotovanou anotací @javax.persistence.Entity. Takto anotované třídy jsou přidány k množině tříd perzistentní jednotky. Je možné ještě přidat další soubory JAR, které chcete, aby byly také skenovány. Takové soubory se přidají do elementu <jar-file> <persistence> <persistence-unit name="titan"> <jta-data-source>java:/OracleDS</jta-data-sou rce> <jar-file>../lib/customer.jar</jat-file> <properties> <property name="org.hibernate.hbm2ddl">upd ate</property> </properties> </persistence-unit> </persistence>

Skenování souborů JAR je garantováno v prostředí Java EE a nemělo by být problémem v prostředí Java SE. V každém případě je možné třídy deklarovat explicitně s použitím elementu <class>: <persistence> <persistence-unit name="titan"> <jta-data-source>java:/OracleDS</jta-data-sou rce> <class>com.titan.domain.Cabin</class> <class>com.titan.domain.Customer</class> <properties> <property name="org.hibernate.hbm2ddl">upd ate</property> </properties> </persistence-unit> </persistence>

Třídy Cabin a Customer jsou přidány do množiny perzistentní jednotky spolu s ostatními třídy získanými z archivní jednotky. Pokud nechceme, aby byl skenován soubor JAR persistence.xml, pak se použije element <excluded-unlisted-classes> <persistence> <persistence-unit name="titan"> <jta-data-source>java:/OracleDS</jta-data-sou rce> <class>com.titan.domain.Cabin</class> <class>com.titan.domain.Customer</class> <excluded-unlisted-classes/> <properties> <property name="org.hibernate.hbm2ddl">upd ate</property> </properties> </persistence-unit> </persistence>

3.5 Získání EntityManager V Javě SE jsou instance Entitních Managerů vytvářeny pomocí @javax.persistence.EntityManagerFactory V Java EE je možné také použít rozhraní factory, ale Java EE poskytuje některé další možnosti k získání instancí je snadnější. EntityManager-Factory package javax.persistence;

Page 28: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

28

public interface EntityManagerFactory { EntityManager createEntityManager(); EntityManager createEntityManager(java.util.Map map); // pro další rozší ření specifikací persistence.xml void close(); boolean isOpen(); }

EntityManager-Factory v Javě SE public class Persistence { public static EntityManagerFactory createEntity ManagerFactory( String unitName ); public static EntityManagerFactory createEntit yManagerFactory( String unitName, java.util.Map properties; );

Třída javax.persistence.Persistence vyhledá popisovač rozmístění persistence.xml podle unitName. To umožní lokalizovat EntityManagerFactory, která odpovídá danému jménu. EntityManagerFactory Javě EE V Javě EE získání instancí může být přímo injektováno do položky nebo metody set() pomocí anotace @javax.Persistence.PersistenceUnit package javax.persistence; @Target({METHOD, FIELD, TYPE})@Retention(RUNTI ME) public @interface PersistenceUnit { String name() default””; String unitName() default””; }

EntityManagerFactory Při použití PersistenceUnit ta nejen injektuje EntityManagerFactory, ale také registruje odkaz na ni uvnitř JNDI ENC v EJB. Kontejner EJB je zodpovědný označení anotace @PersistenceUnit a injektování odpovídající factory – viz následující kód:

import javax.persistence.*; import javax.ejb.* ; @Stateless public MyBean implements MyBusinessInterface { @PersistenceUnit(unitName= ”CRM”) private EntityManagerFactory factory; private EntityManagerFactory factory2; @PersistenceUnit(unitName=”CUSTDB”) public void setFactory2(EntityManagerFactory f) { this.factory2 = f; }

Page 29: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

29

Když je vytvořena instance bezstavového beanu, kontejner EJB nastaví datový atribut factory na „CRM“, definované v persistence unit. Také volá setFactory2() s persistence unit „CUSTDB“. V prostředí Java EE není třeba volat metodu close(). Získání perzistentního kontextu Perzistentní kontext může být vytvořen voláním EntityManagerFactory.createEntityManager(). Vrácená instance reprezentuje rozšířený perzistentní kontext. Je-li v EntityManagerFactory nastaveno JTA-enable, pak je možné explicitně získat instanci EntityManager uvnitř transakce voláním metody EntityManager.joinTransaction(). Pokud nezískáte EntityManager uvnitř transakce, potom změny provedené v entitách nejsou synchronizovány s obsahem databáze. Použití API EntityManagerFactory je trochu upovídané a může být těžkopádné při vnořené EJB volání. Naštěstí EJB a specifikace Java Persistence jsou výborně integrované. EntityManager může být přímo injektovaný do EJB použitím anotace. @javax.persistence.PersistenceContext (jako ekvivalent XML).

package javax.persistence; public enum PersistenceContextType { TRANSACTION, EXTENDED } public @interface PersistenceProperty { String name(); String value(); } @Target{METHOD, TYPE, FIELD}) @Retention(RUNTIME) public @interface PersistenceContext { String name() default “”; String unitName() default””; PersistenceContextType type() default TRANSACTIO N; PersistenceProperty[] properties() default {}; }

Anotace @PersistentContext funguje víceméně stejným způsobem jako @PersistenceUnit kromě toho, že místo instance EntityManagerFactory je injektovaná instance entitního manažera: @Stateless public class MySessionBean implements MySessioinR emote { @PersistenceContext(unitName=”titan”) private EntityManager entityManager; . . . }

Dependency injection Je to výkonný mechanismus pro získání referencí na zdroje a pro injektování referencí objektům v relaci s EJB. Injection je technika spolehnutí se na kontejner, který poskytne

Page 30: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

30

ovladače k objektům ke kterým má přístup. Dříve bylo třeba zdroje vyhledávat pomocí JNDI. Nyní pouze definujeme reference a používáme objekty. Kontejner injektuje reference před vašim voláním metod. Získání perzistentního kontextu Atribut unitName() identifikuje perzistenci. Perzistentní kontext ohraničený transakcí je defaultně injektovaný, když se používá tato anotace. Je možné zastínit (předefinovat) default s atributem type(). Když zpřístupníme tento transakcí omezený EntityManager, perzistentní kontext bude asociovaný s transakcí, dokud neskončí. To znamená, že pokud jste v interakci s entitními managery uvnitř kontextu transakce, nezáleží na tom, zda to jsou odlišné instance, které jsou injektovány do různých beanů. Bude použit stejný perzistentní kontext. EXTENDED entitní manager může být použit jen pro stavové session beany. @Stateful public class MyStatefulBean implements MyStatefulR emote { @PersistenceContext(unitName=”titan”, type=PersistenceContextType.EXTENDED) private EntityManager manager ; . . . }

Když je vytvořen MyStatefulBean, je vytvořen také perzistentní kontext pro injektovaný atribut manager. Perzistentní kontext má stejnou životnost jako bean. Když je stavový session bean odstraněn, perzistentní kontext je uzavřen.

package javax.persistence; public interface EntityManager { public void persist(Object entity); public <T> T find(Class <T> entityClass, Object primaryKey); public <T> T getReference(Class <T> entityClass, Object primaryKey); public <T> T merge(T entity); public void remove(Object entity) ; public void lock(Object entity, LockModeType loc kMode); public void getReference(Object entity); public Boolean contains(Object entity); public void clear(); public void joinTransaction(); public void flush(); public FlushModeType getFlushMode(); public void setFlushMode(FlushModeType type); public Query createQuery(String queryString); public Query createNamedQuery(String name): public Query createNativeQuery(String sqlString) ; public Query createNativeQuery(String sqlString, String res ultSetMaping); public Query createNativeQuery(String sqlString, Class resultClass); public Object getDelegate(); public void close(); public Boolean isOpen();}

Page 31: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

31

Perzistentní entity Učinit entity perzistentní znamená v terminologii EJB jejich vložení do databáze. K vytvoření entity je třeba nejdříve alokovat paměť pro její instanci (metoda new()), nastavit datové atributy a propojit relace, který může mít instance s dalšími objekty (stejné jako v Javě). Pak je třeba vyvolat metodu persist() EntityManageru. Customer cust = new Customer(); cust.setName(„Bill“); entityManager.persist(cust);

Po vyvolání metody, zařadí EntityManager instanci do fronty na zapsání do databáze. Skutečné vložení instance do databáze je závislé na několika věcech. • Je-li metoda persist() vyvolaná uvnitř transakce, je vložení vykonáno ihned, nebo na

konci vlastní transakce – závisí na tzv. flush módu (kdy se provádí konkrétní zápisy). • Zápis může být vyvolán uvnitř transakce „manuálně“, vyvoláním metody flush(). • Při volání metody persist() mimo transakci, (pouze v rozšířeném perzistentním kontextu),

zápis instance do databáze se uloží do fronty požadavků a provede se teprve až se perzistentní kontext asociuje s transakcí.

EntityManager nabízí dvě možnosti vyhledání: 1. Vyhledání entity pomocí zadaného primárního klíče. 2. Vyhledání entit pomocí dotazu (executing query). Metody find() a getReference() hledání podle primárního klíče EntityManager má dvě metody na vyhledání entit pomocí primárního klíče: public interface EntityManager { <T> T find(Class<T> entityClass, Object primary Key); <T> T getReference(Class>T> entityClass, Object primaryKey); }

Obě metody mají stejné argumenty – entitní třídu a primární klíč. Java využívá parametrizované třídy (generics) a proto není třeba provádět přetypování (casting). Metoda find() vrací null, pokud se entita v databázi nenašla. Také inicializuje stav, založený na politice lazy-loading, pro každý datový atribut (lazy-loading: entita není dodána jako celek, ale podle požadovaných datových atributů – vlastností). Customer cust = entityManager.find( Customer.cl ass, 2);

primitivní typ 2 – int je automaticky převeden na Integer, to z důvodu, pokud by metoda find() očekávala parametr typu Object Customer cust = null; try { cust = entityManager.getReference(Customer.class , 2); } catch (EntityNotFoundException notFound) { // obnoveni logiky }

Metoda getReference() se liší od předchozí metody při nenalezení entity v databázi. Metoda v tomto případě vyhazuje výjimku javax.persistence.EntityNotFoundException. Obě metody

Page 32: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

32

mohou být vyvolány mimo transakci, ale v tom případě pouze v rozšířeném perzistentním kontextu. Vytváření a provádění EJB QL dotazů je analogické vytváření s spouštění JDBC příkazů: Query query = entityManager.createQuery(“from Custo mer c where id=2”); Customer cust = (Customer)query.getSimpleResult();

Všechny instance vrácené uvedenými třemi metodami zůstávají řízenými (managed) po dobu aktivity perzistentního kontextu vrámci něhož byly zpřístupněny. To znamená, že další volání metod vrátí stejné entitní objekty. Aktualizace entit updating Aktualizaci entity je možné provést po jejím vyhledání – metody find(), getReference() nebo provedením dotazu (query), v době, kdy je entita řízena perzistentním kontextem (tento perzistentní kontext není uzavřen). Čas konkrétní aktualizace je závislý na metodě flush(), nebo můžete vyvolat flush() přímo. @PersistenceContext EntityManager entityManager; @TransactionAttribute(REQUIRED) public void updateBedCount(int id, int newCoint) { Cabin cabin = entityManager.find(Cabin.class , id); cabin.setBedCount(newCount); }

Opětovné vkládání entit (merging entities) Činnost ukážeme na příkladě, kde klient volá metodu vzdáleného session beanu TravelAgent k nalezení instance cabin v databázi: @PersistenceContext EntityManager entityManager; @TransactionAttribute(REQUIRED) public Cabin findCabin(int id) { return entityManager.find(Cabin.class, id); }

V uvedeném příkladě perzistentní kontext končí s ukončením metody findCabin(), protože se jedná o jednoduchou JTA transakci. Když je instance entity Cabin serializovaná, je odpojena od entity manageru a poslána vzdálenému klientovi. Klient může provést změny a zpět poslat instanci na server pro uložení do databáze. Následuje kód vzdáleného klienta: Cabin cabin = travelAgent.findCabin(1); cabin.setBenCount(4); travelAgent.updateCabin(cabin);

Metoda updateCabin() vezme parametr a sloučí entitní instanci zpět v aktuálním perzistentním kontextu entitního manageru voláním metody merge(): @PersistenceContext EntityManager entityManager; @TransactionAttribute(REQUIRED) public void updateCabin(Cabin cabin) { Cabin copy = entityManager.merge(cabin); }

Page 33: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

33

Změny, které provedl vzdálený klient nyní budou provedeny v databázi. Mohou nastat dvě alternativy: 1. Entitní manager již nepracuje s instancí beanu Cabin se stejným ID. Pak je vytvořena plná

kopie parametru cabin a vrácena z metody merge(). Tuto kopii pak řídí entitní manager a změny v databázi budou provedeny v závislosti na metodě flush(). Parametr cabin zůstane nepřipojen a neřízen.

2. Pokud entitní manager pracuje s instancí paramentu cabin se stejným primárním klíčem, potom obsah parametru cabin je zkopírován do řízeného objektu instance. Metoda merge() vrací řízenou instanci. Parametr cabin zůstane nepřipojen a neřízen.

Odstraňování entit (removing entities) Entita může být odstraněna voláním EntityManager.remove(). Skutečné odstranění je závislé na metodě flush(). @PersistenceContext EntityManager entityManager; @TransactionAttributes(REQUIRED) public void removeCabin(int id) { Cabin cabin = entityManager.find(Cabin.class, id); entityManager.remove(cabin); }

Po vyvolání metody remove() je instance cabin již neřízena a bude odpojena. Její možné vazby na další objekty mohou být součástí kaskádního odstranění objektů. Pokud by bylo třeba opět obnovit zrušenou instanci cabin, je třeba použít metodu persist(). Metoda refresh() Tato metoda se používá k aktualizaci právě zpracovávané entity vzhledem k databázi (podobně jako aktualizace internetového prohlížeče). Instance má změněné datové atributy podle databáze. @PersistenceContext EntityManager entityManager; @TransactionAttributes(REQUIRED) public void removeCabin(int id) { Cabin cabin = entityManager.find(Cabin.class , id) ; entityManager. refresh (cabin); }

Metody contains() a clear() Metoda contains() vezme jako parametr instanci entity. Je-li tato instance aktuálně řízena perzistentním kontextem, metoda vrací true. Jestli potřebujete odpojit všechny instance řízených entit od perzistentního kontextu, můžete vyvolat metodu clear() entitního manageru. Všechny provedené změny v entitních instancích se však ztratí. Proto je doporučeno nejdříve provést metodu flush() a následně pak metodu clear(). Metoda flush() a FlushModeType Všechny změny v databázi se provedou, až EntityManager vykoná metodu flush(). Je samozřejmě možné explicitní vyvolání metody. Standardně je metoda flush() vyvolávaná automaticky před query a v době ukončování transakce. Změnu tohoto chování je možné řídit pomocí výčtové třídy javax.persistence.FlushModeType: public enum FlushModeType { AUTO,

Page 34: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

34

COMMIT }

Auto je defaultní chování popsané dříve. COMMIT znamená, že se změny provádí až se skončením transakce, tedy ne před query (dotazem).

Perzistence a její implementace je mimo jiné to čím se verze 3 EJB liší od předchozích verzí. Práce je jednodušší a přehlednější, díky využívání tzv. POJO objektů jazyka Java. Dále tato kapitola popisuje EntityManager, který je zodpovědný za organizaci perzistence a jeho služby. V této části je také vysvětlen vztah entitních beanů a POJO objektů.

Page 35: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

35

4 Mapování perzistentních objektů

V této kapitole se dozvíte: • jak probíhá mapování standardních javovských objektů na perzistentní objekty, • jak je využit primární klíč, • jak probíhá mapování jednotlivých položek. Protože ve specifikaci Java Persistence jsou entity obyčejné javovské třídy, pracuje se s nimi jako s jinými třídami v Javě. EntityManager prostřednictvím svých metod persist(), update(), remove(), locate() a query zabezpečí provádění uvedených operací s entitami v relační databázi. Entita Customer je jednoduchý entitní bean, který modeluje koncept zákazníka, nebo pasažéra na záoceánské lodi. Je navržen tak, aby jeho struktura vyhovovala mnoha komerčním doménám. Entita Customer bude refaktorována mnoha různými způsoby, které mohou být mapovány do relačních databází.

import javax.persistence.*; @Entity public class Customer implements java.io.Serializab le { //@Id private int id; private String firstName; private String lastName; @Id public long getId() { return id; } public void setId(long pk) {id = pk; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this. lastName = lastName; } }

Typy metadat Specifikace Java Persistence vyžaduje pouze dva typy metadat při vytváření perzistentní třídy: • anotace (komentář) @javax.persistence.Entity označuje, že třída bude mapovaná do vaší

databáze • anotace @javax.persistence.Id označuje, který datový atribut třídy bude brán jako

primární klíč.

Page 36: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

36

Tabulka Customer Správce perzistence dále bude předpokládat, že všechny ostatní datové atributy ve třídě budou mapovány do sloupců stejného jména a typu jako ve třídě. Jméno tabulky bude defaultně nekvalifikovaným jménem třídy bean. Definice tabulky, kterou správce persistence očekává pro mapování: create table Customer( id long primary key not null, firstName VARCHAR(255), lastName VARCHAR(255) );

Anotace @javax.persistence.Entity říká správci perzistence, že vaše třída může být ukládána do relační databáze (může být perzistentní): package javax.persistence; @Target(TYPE) @Retention(RUNTIME) public @interface Entity { String name() default “”; }

Anotace @Entity má jeden atribut name(). Tento atribut je použit jako reference uvnitř EJB QL výrazu. Použití anotace @javax.persistence.Id určuje, zda budete používat pro deklaraci perzistentních atributů styl Java bean, nebo styl Java fields. Jestli umístíte anotaci @Id na metodu get (viz předchozí příklad), pak musíte aplikovat jakékoli jiné anotace mapování rovněž na get() a set() metody. Správce perzistence bude dále předpokládat, že jakékoli další get() a set() metody reprezentují perzistentní atributy (vlastnosti) a bude je automaticky mapovat na základě jejích jména a typu. @Entity public class Customer implements java.io.Seriali zable { @Id private long id; private String firstName; . . . }

Anotace @Id je umístěna v deklaraci datových atributů třídy. Správce perzistence bude také předpokládat, že jakýkoli datový atribut třídy je také perzistentním atributem a bude ho automaticky mapovat na základě jeho jména a typu. Jakákoli anotace mapování musí být umístěna v oblasti datových atributů, ne v oblasti metod get a set. Mapovací soubor XML Pokud nechcete používat anotace k identifikaci a mapování vašeho entitního beanu, alternativně můžete použít mapovací soubor XML, aby deklaroval metadata. Implicitně se správce perzistence podívá do adresáře MATA-INF na soubor orm.xml, nebo je možné deklarovat mapovací soubor v elementu <mapping-file> v souboru persistence.xml. <entity-mapping> <entity class="com.titan.domain.Customer" acce ss="PROPERTY"> <attributes> <id name="id"/> </attributes> </entity>

Page 37: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

37

</entity-mapping>

Základní relační mapování Pro implementaci entitního beanu má vývojář dvě možnosti. Postupovat z objektového modelu Javy a odvodit schéma relační databáze z tohoto modelu, anebo použít existující relační schéma databáze a z něho odvodit objektový model v Javě. Elementární mapování Předpokládejme, že máme relační tabulku a tu chceme mapovat do entity Customer a přitom využít anotace @javax.persistence.Table a @javax.persistence.Column. Definice tabulky v SQL je následující: create table CUCTOMER_TABLE ( CUST_ID integer primary key not null, FIRST_NAME varchar(20) not null lastName varchar(255) not null, );

Chceme změnit jméno tabulky, jména sloupců u id a firstName. Dále chceme, aby firstName mělo not-null omezení a délku nastavit na 20 znaků. Musíme proto modifikovat původní entitní třídu Customer následovně:

import javax.persistence.*; @Entity @Table(name=”CUSTOMER_TABLE”) public class Customer implements java.io.Serializab le; private long id; private String firstName; private String lastName; @Id @Column(name=”CUST_ID, nullable=false, columnDef inition=”integer”) public long getId() { return id; } public void setId(long id) { this.id = id; } @Column(name=”FIRST_NAME, length=20, nullable=fa lse) public String getFirstName() { return firstName; } public vois setFirstName(String firstName) {this .firstName = firstName; } public String getLastName() {return lastName; } public void setLastName(String lastName) { this. lastName = lastName; } }

Anotace @javax.persistence.Table říká službě entitního managera jméno tabulky, do které se bude bean mapovat. Tuto anotaci nemusí být deklarovaná, pokud chcete nechat tabulce nekvalifikované jméno beanu. Plná deklarace anotace: package javax.persistence; @Target({TYPE}) @Retention(RUNTIME) public @interface Table

Page 38: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

38

{ String name() default “”; String catalog() default “”; String schema() default “”; UniqueConstraint[] uniqueCinstraints() defaul t {}; }

Atributy catalog() a schema() odpovídají pojmům z relačních databází. public @interface UniqueConstraint { String[] columnNames(); }

Atribut @Table.uniqueConstraints() dovoluje specifikovat omezení sloupců, jenž by měla být včleněna do generovaného Data Definition Language (DDL). Anotace @javax.persistence.Column popisuje, jak je konkrétní atribut mapovaný do konkrétního sloupce tabulky: public @interface Column { String name() default “”; boolean unique() default false; boolean nullable() default true; boolean insertable() default true; boolean updatable() default true; String columnDefinition() default “”; String table() default “”; int length() default 255; int precision() default 0; int scale() default 0; }

Atribut name() specifikuje jméno sloupce, je-li nespecifikované, pak je rovné jménu atrubutu v entitní třídě. Atribut table() se využívá při specifikaci mapování více tabulek Anotace @javax.persistence.Id identifikuje jeden nebo více datových atributů, které vyváří klíč pro tabulku: package javax.persistence; @Target ({METHOD, FIELD}) @Retention(RUNTIME) public @interface ID { }

Vytvářet primární klíče můžete sami, manuálně, nebo to může dělat automaticky správce perzistence. Při automatickém generování primárního klíče musíte použít: package javax.persistence; @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface GeneratedValue { GenerationType strategy() default AUTO; String generator() default “”;

Page 39: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

39

} public enum GenerationType { TABLE, SEQUENCE, IDENTITY, AUTO }

Atribut strategy() slouží pro definování typu primárního generátoru. Nejpoužívanější strategií je GeneratorType.AUTO, která používá konfiguraci:

import javax.persistence.*; @Entity @Table(name=”CUSTOMER_TABLE”) public class Customer implements java.io.Serializa ble; private long id; private String firstName; private String lastName; @Id @GeneratedValue public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName ; } public vois setFirstName(String firstName) { this.firstName = firstN ame; } public String getLastNAme() {return lastName; } public void setLAstNAme(String lastName) { this .lastName = lastName; } }

Strategie AUTO říká správci perzistence, že může generovat primární klíč automaticky pro uživatele. Strategie IDENTITY používá speciální typ sloupce dostupný v mnoha databázových implementacích pro vytváření primárního klíče. 4.1 Primární klíč – třídy a kompozitní klíče Někdy situace vyžaduje, aby byl primární klíč složen z vícerých perzistentních položek (datových atributů). Např. bychom potřebovali, aby byl primární klíč tvořen příjmením a položkou social security number. Tomu se říká složený (composite ) klíč. Jedna z možných cest jak mapovat tento typ modelu, je použít anotaci @javax.persistence.EmbeddedId. K definování třídy primary-key je třeba použít anotaci @IDClass. Třída bean bude tento složený klíč využívat při interakci s entity managerem, při vyhledávání perzistentních objektů prostřednictvím tohoto klíče. @Target(TYPE) @Retention(RUNTIME) public @interface IdClass { Class value(); }

Page 40: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

40

S využitím anotace @Id stanovíte jeden, nebo více datových atributů, ze kterých bude složen primární klíč. Tyto atributy (vlastnosti) se musí přesně mapovat do atributů (vlastností) třídy @IdClass. Ve třídě Customer bude vytvořen složený klíč z atributů (vlastností) last name a social security numer:

public class CustomerPK implements java.io.Serializ able { private String lastName; private long ssn; public CustomerPK() {} public CustomerPK(String lastName, long ssn) { this.lastName = lastName; this.ssn = ssn; } public String getLastName() { return this.lastNa me; } public void setLastName(String lastName) { this. lastName = lastName; } public long getSsn() { return ssn; } public void setSsn(long ssn) { this.ssn = ssn; } public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof CustomerPK)) return fals e; CustomerPK pk = (CustomerPK)obj; if (!lastName.equals(pk.lastName)) return fal se; if (ssn != pk.ssn) return false; return true; } public int hashCode() { return lastName.hashCode() + (int)ssn; } }

Na primární klíč jsou následující požadavky: • musí být serializovatelný, • musí mít public bezparametrický konstruktor, • musí implementovat metody equal() a hashCode(). Bean Customer musí mít přesně stejné atributy (vlastnosti) jako třída CustomerPK a tyto atributy (vlastnosti) jsou uvedeny za anotací @Id.

import javax.persistence.*; @Entity @IdClass(CustomerPK.class) public class Customer implements java.io.Serializab le { private String firstName; private String lastName; private long ssn;

Page 41: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

41

public String getFirstName() { return firstName; } public void setFirstName(String firstName) { thi s.firstName = firstName; } @Id public String getLastName() { return lastName; } public void setLastName(String lastName) { this. lastName = lastName; } @Id public long getSsn() { return ssn; } public void setSsn(long ssn) { this.ssn = ssn; } }

Primární klíč se používá, kdykoli je dotaz na Customer: CustomerPk pk = new CustomerPK(“Burke”, 9999999 ); Customer cust = entityManager.find(Customer.cla ss, pk);

4.2 Mapování položek – (Property Mapping) Zatím jsme se zabývali, jak specifikovat sloupce mapování na jednoduché primitivní typy. Existují ještě další metadata umožňující komplexnější mapování datových atributů (properties). Java Persistence má mapování pro JDBC Blobs a Clobs, serializované objekty, vnořené objekty, stejně jako optimistickou konkurenci s verzí jako vlastností. Anotace @Transient slouží k označení vlastností, které nebudou perzistentní. Implicitně jsou všechny perzistentní, což se využívá při rychlém prototypování. Příkladem může být rozhraní, které umožňuje získat instanci třídy CustomerPK pro informaci o vlastnosti lastName a Social Security numer. Je dobré mít přístupovou metodu ve třídě Customer. Vlastnosti označené jako @javax.persistence.Transient nejsou brány entityManagerem jako perzistentní.

import javax.persistence.*; @Entity public class Customer implements java.io.Serializab le { private String firstName; private CustomerPK pk; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @Transient public String getLastName() { return pk.getLastN ame(); } @Transient public long getSsn() { return pk.getSsn(); } @EmbeddedId public CustomerPK getPk() { return pk; } public void setPk(CustomerPK pk) { this.pk = pk; } }

Page 42: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

42

Anotace @Basic je nejjednodušší formou mapování perzistentních vlastností. Je to defaultní typ mapování pro primitivní typy, primitivní obalové typy. Mapování základních typů není třeba to explicitně sdělovat EntityManageru, protože ten to provede sám podle typu atributu. public @intrerface Basic { FetchType fetch() default EAGER; boolean optimal() default true; } public enum FetchType { LAZY, EAGER }

Anotace @Temporal poskytuje dodatečné informace správci perzistence ohledně mapování vlastností (atributů) java.util.Date nebo java.util.Calendar. Tato anotace dovoluje mapovat tyto objektové typy na date, time nebo timestamp. package javax.persistence; public enum TemporalType { DATE, TIME, TIMESTAMP } public @interface Temporal { TemporalType value() default TIMESTAMP; }

Vícenásobné mapování s @SecondaryTable Někdy je potřebné jeden logický objekt rozdělit do dvou databázových tabulek. K tomu účelu je anotace @javax.persistence.SecontadyTable. Například bean Customer má atribut definující jeho adresu. Tabulky budou mít následující tvar: create table CUSTOMER_TABLE ( CUST_ID integer Primary Key Not Null, FIRST_NAME varchar(20) not null, LAST_NAME varchar(50) not null ); create table ADDRESS_TABLE ( ADDRESS_ID integer primary key not null, STREET varchar(255) not null, CITY varchar(255) not null, STATE varchar(255) not null };

Při použití anotace @SecondaryTable, primární klíč ADDRESS_TABLE musí být spojitelný s jedním nebo více sloupci v CUSTOMER_TABLE. public @interface SecondaryTable { String name(); String catalog() default “”; String schema() default “”; PrimaryKeyJoinColumn[] pkJoinColumns() defaul t (); UniqueConstraint[] uniqueConstraints() defaul t (); } public @interface PrimaryKeyJoinColumn ( String name() default “”; String referencedColumnName() default “”; String columnDefinition() default “”; }

Page 43: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

43

Anotace @SecondaryTable je podobná anotaci @Table s dodatečným atributem pkJoinColumns(). Ve třídě Customer bean je třeba definovat tuto anotaci a specifikovat, že primární klíč ADDRESS_TABLE použil vloženou anotaci @PrimaryKeyJoinColumn. Atribut name() anotace @PrimaryKeyJoinColumn reprezentuje sloupec v ADDRESS_TABLE, který bude použit ve spojení. Atribut referencedColumnName() reprezentuje jméno sloupce v CUSTOMER_TABLE, které je použito pro propojení v ADDRESS_TABLE. import javax.persistence.*; import com.acme.imaging.JPEG ; @Entity @Table(name= “CUSTOMER_TABLE”) @SecondaryTable(name= “ADDRESS_TABLE”, pkJoinColumns={ @PrimaryKeyJoinColumn(name =”ADDRESS_ID”))) public class Customer implements java.io.Serializab le { . . .

@PrimaryKeyColumn specifikuje sloupec v ADDRESS_TABLE, který bude spojen s primárním klíčem v CUSTOMER_TABLE, v tomto případě ADDRESS_ID. Jení nutné specifikovat atribut referencedColumnName() této anotace, protože tom je defaultně nastaveno na sloupec primárného klíče v entitě Customer. Dalším krokem je namapovat vlastnosti street, city a state na sloupce v ADDRESS_TABLE. K tomu se použije jeden z atributů anotace @Column.

Mapování perzistentních objektů představuje také důležitou část technologie EJB. Perzistentní objekt se vlastně chápe objekt uložený do relační databáze. K tomu existují postupy, které jsou vysvětleny v této kapitole. Dále tato část popisuje mapování položek.

Page 44: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

44

5 Entitní relace

V této kapitole se dozvíte: • jaké jsou základní entitní relace, • k čemu se používá a jak probíhá kaskádování.

Mezi entitními beany existuje sedm typů relací. Existují čtyři typy kardinality: one-to-one, one-to-many, many-to-one, many-to-many. Navíc každá relace může být jednosměrná nebo obousměrná. To přináší celkem osm možností, ale pokud se podíváme na relaci one-to-many a many-to-one obousměrné relace jsou tyto relace totožné. 5.1 One-to-one jednosměrná relace Příkladem takové relace je relace mezi entitou customer a entitou address. Každý Customer právě jednu Address a právě jedna Address má jednoho Customer. Entita která má referenci určuje navigabilitu (směrování). Entita Customer má referenci na entittu Address, proto se jedná o jednosměrnou relaci. Z entity Customer je možné se dostat do entity Address, opačný směr není možný. Schéma relační databázové : • Tabulka CUSTOMER obsahuje cizí klíč do tabulky ADDRESS, tabulka ADDRESS

neobsahuje cizí klíč do tabulky CUSTOMER. Programový model: • Entitní bean Customer má atribut (vlastnost), který umožňuje relaci. K zpřístupnění/

modifikaci se uvnitř třídy Customer využívají metody getAddress() / setAddress(). package com.titan.domain; @Entity public class Customer implements java.io.Serializab le { . . . private Address homeAddress; . . . @OneToOne(cascade={CascadeType.All}) @JoinColumn(name=”ADDRESS_ID”) public Address getAddress() { return homeAddress ; } public void setAddress(Address address) { homeAd dress = address; } public @interface JoinColumn { String name() default “”; String referencedColumnName() default “”; boolean unique() default false; boolean nullable() default true; boolean updatable() default true; String columnDefinition() default “”; String table() default “”; }

Page 45: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

45

Pokud se ke spojení nevyužívá sloupec primárního klíče tabulky ADDRESS, pak je třeba použít atributu referencedColumnName(), který musí být jedinečný. Pokud má relační entita složený primární klíč, je třeba použít anotaci @JoinColumns k definování vícenásobných sloupců cizího klíče: public @interface @JoinColumns { JoinColumn[] value(); } public @interface OneToOne { Class targetEntity() default void.class; CascadeType[] cascade() default {}; // bud e vysv ětleno pozd ěji FetchType fetch() default EAGER; boolean optional() default true; String mappedBy() default “”; // bude vys větleno pozd ěji }

Atribut targetEntity() reprezentuje třídu entity, na kterou máte relaci. Pokud není inicializovaná, udělá to správce perzistence. Atribut fetch() dovoluje specifikaci lazy nebo eager nahrávání z perzistentní paměti. Atribut optional() specifikuje, zda může být relace null. Je-li nastaven na false, musí mezi entitami existovat relace not-null. Spojovací sloupce primárního klíče Někdy jsou místo konkrétního spojovacího sloupce použity primární klíče dvou relačních entit (jsou ve vzájemné relaci). V tom případě jsou primární klíče relačních entit identické a není třeba určovat specifický spojovací sloupec. Při tomto mapování je třeba použít alternativní anotace @javax.persistence.PrimaryKeyJoinColumn: public @interface PrimaryKeyJoinColumn { String name() default “”; String referenceColumnName() default “”; String columnDefinition() default “”; }

Atribut name() odkazuje na jméno sloupce primárního klíče entity, na kterou je použita anotace. Nejedná-li se o složený klíč - prázdný atribut doplní, správce perzistence. Atribut referencedColumnName() je sloupec, na který se má napojit relační entita. Zůstane-li prázdný, použije se primární klíč relační entity. Atribut columnDefinition() bude použit, pokud správce perzistence generuje schéma, a to bude specifikovat typ SQL referencedColumnName(). Je-li spojovací primární klíč složený, pak se používá anotace @javax.persistence.PrimaryKeyJoinColumns: public @interface PrimaryKeyJoinColumns { PrimaryKeyJoinColumn[] value(); }

Mapování Customer/Address v relaci one-to-one @Entity public class Customer implements java.io.Serializab le { . . . private Address homeAddress; . . . @OneToOne(cascade={CascadeType.All})

Page 46: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

46

@PrimaryKeyJoinColumn // není použit složený kl í č public Address getAddress() { return homeAddress; } public void setAddress(Address address) { this.homeAddress = address; }

Defóltní mapování relací Pokud správce persistence podporuje vytváření auto schéma, není třeba specifikovat metadata jako @JoinColumn nebo @PrimaryKeyJoinColumn. Generování auto schéma je výhodné zejména při rychlém prototypování. Pokud není specifikované žádné databázové mapování pro jednosměrnou one-to-one relaci, správce perzistence vytvoří nezbytné cizí mapovací klíče. @OneToOne public Address getAddress() { return homeAddress } . . .

5.2 Obousměrná relace one-to-one Pro ilustraci této relace můžeme použít entity Customer a Credit_Card. Entita Customer musí vědět o své entitě Credit_Card stejně jako je dobré, aby entita Credit_Card věděla o své entitě Customer. Jedna z tabulek má cizí klíč, který odkazuje na druhou tabulku. Relační databázové schéma: CREATE TABLE CREDIT_CARD ( ID INT PRIMARY KEY NOT NULL, EXP_DATE DATE, NUMBER CHAR(20), NAME CHAR), ORGANIZATION CHAR(20) ) CREATE TABLE CUSTOMER ( ID INT PRIMARY KEY NOT NULL, LAST_NAME CHAR(20), FIRST_NAME CHAR(20), ADDRESS_ID INT, CREDIT_CARD_ID INT )

Programový model K modelování relací mezi entitami Customer a Credit_Card potřebujeme deklarovat v obou třídách atributy, které budou odkazovat na opačné třídy: @Entuty public class Credit_Card implements java.io.Seriali zable { private int id; private Date expiration; private String number; private String name; private String organization; private Customer customer; . . . @OneToOne(mappedBy=”creditCard”) public Customer getCustomer() { return this.cust omer; } public void setCustomer(Customer customer) { this.customer = customer;

Page 47: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

47

} . . . }

Atribut mappedBy(), který je zde nový, nastavuje dvousměrnou relaci a říká perzistentnímu manažeru, že informace pro mapování této relace do našich tabulek je specifikovaná ve třídě Customer bean, kontrétně v atributu creditCard. Také je třeba přidat datový atribut relace do třídy Customer bean: @Entity public class Customer implements java.io.Serializab le { private CreditCard creditCard; . . . @OneToOne(cascade={CascadeType.All}) @JoinColumn(name=CREDIT_CARD_ID) public CreditCard getCreditCard() { return credi tCard; } public void setCreditCard(CreditCard card) { this.creditCard = card; } . . . }

Atribut cascade() je nastaven na ALL. To způsobí kaskádní vytvoření CreditCard, když je entita Customer vytvořena perzistentní. Pokud je v entitě použit atribut mappedBy() znamená to, že daná entita je tzv. inverzní stranou (inverse side) relace (viz entita CreditCard). Entita Customer je naopak vlastnící stranou (owning side) relace. Pokud by se měla instance CreditCard asociovat s jinou instancí Customer, bylo by třeba nejdříve vyvolat metodu setCreditCard() původní instance Customer, nastavit ji na null a potom vyvolat setCreditCard() s argumentem new Customer (jinou instance entity Customer) Customer newCust = em.find(Customer.class, new CustId); CreditCard card = oldCustomer.getCreditCard(); oldCustomer.setCreditCard(null); newCust.setCreditCard(card);

Při rušení credit card patřící customer, je třeba nastavit atribut creditCard v instanci Customer na null a odstranit instance CreditCard z database: Customer cust = em.find(Customer.class, id); em.remove(cust.getCreditCard(); cust.setCreditCard(null);

Defóltní relační mapování Pokud správce perzistence podporuje vytvoření auto schéma , není třeba používat metadata typu @JoinColumn. V entitní třídě Customer se použije pouze anotace @OneToOne a v entitní třídě CreditCard anotace @OneToOne(mappedBy=“creditCard”). 5.3 Jednosměrná relace one-to-many Příkladem této relace může být např. instance Customer, která má mnoho telefonních kontaktů (telefonních čísel). Tento typ relace vyžaduje po vývojáři pracovat s kolekcí odkazů, místo jednotlivých odkazů.

Page 48: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

48

Programový model Relace one-to many se deklaruje pomocí anotace @javax.persistence.OneToMany: public @interface OneToMany { Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default LAZY; String mappedBy() default““; }

@Entity public class Customer implements java.io.Serializab le; . . . private Collection<Phone> phoneNumbers = new Arr ayList<Phone>(); . . . @OneToMany(cascade={CascadeType.ALL}) @JoinColumn(name=”CUSTOMER_ID”) public Collection<Phone> getPhoneNumbers() { return phoneNumbers; } public void setPhoneNumbers(Collection<Phone> ph ones) { this.phoneNumbers = phones; } }

Definice atributů je podobná jako v případě anotace @OneToOne. Anotace @JoinColumn odkazuje na sloupec CUSTOMER_ID v tabulce PHONE. Následuje třída Phone bean:

import javax.persistence.*; @Entity public class Phone implements java.io.Serializable { private int id; private String number; private byte type; public Phone() {} public Phone(String number, int type) { this.number = number; this.type = type; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getNumber() { return number; } public void setNumber(String number) { this.numb er = number; } public byte getType() { return type; } public void setType(byte type) { this.type = typ e; } }

Příklad použití: Customer cust = entityManager.find(Customer.class, pk); Phone phone = new Phone(“597-092-106”, 5); cust.getPhones().add(phone);

Page 49: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

49

Protože entita Customer je vlastnící strana (owning side), proto správce perzistence automaticky vytvoří new Phone. Pokud je třeba odstranit instanci beanu Phone, je to třeba udělat jak u instance Customer, tak i v databázi: cust.getPhones().remove(phone); entityManager.remove(phone);

Spojené mapování tabulek (join table mapping) Jinou možností relačního databázového mapování pro entitní relaci Customer/Phone by byla asociační tabulka, která udržuje dva sloupce cizích klíčů ukazujících na záznamy CUSTOMER a PHONE. Na cizí klíč u entity PHONE je možné nastavit omezení na jedinečnost záznamů (každé telefonní číslo je jedinečné). Tvar asociační tabulky: create table CUSTOMER_PHONE ( CUSTOMER_ID int not null, PHONE_ID int not null unique );

Dále je potřebné ještě změnit anotaci @JoinColumn v entitě Customer použitím anotace @javax.persistence.JoinTable: public @interface JoinTable { String name() default “”; String catalogue() default “”; String schema() default “”; JoinColumn[] joinColumns() default {}; JoinColumn[] inverseJoinColumns() default { }; UniqueConstraint[] uniqueConstraints() defa ult {}; }

Atribut joinColumns() by měl definovat mapování cizího klíče na primárnmí klíč vlastnící strany relace. Atribut inverseJoinColumns() mapuje nevlastnící stranu relace. Pokud by některá ze stran relace měla složený primární klíč, museli bychom přidat další anotaci @JoinColumn do pole: . . . @OneToMany(cascade={CascadeType.ALL}) @JoinTable(name=”CUSTOMER_PHONE”), joinColumns=(@JoinColumn(name=”CUSTOMER_ID ”)}, inverseJoinColumns={@JoinColumn(name=”PHON E_ID”)})

. . .

Touto definicí je řečeno, že primární klíč pro Customer se mapuje do spojeného sloupce CUSTOMER_ID v tabulce CUSTOMER_PHONE. Primární klíč entity Phone se mapuje do spojeného sloupce PHONE_ID v tabulce CUSTOMER_PHONE. Omezení na jedinečnost bude nastaveno pro sloupec PHONE_ID v tabulce CUSTOMER_PHONE správcem perzistence. Defóltní relační mapování Pokud správce perzistence podporuje vytváření auto schéma, není třeba specifikovat metadata @JoinColumn. @Entity

Page 50: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

50

public class Customer implements java.io.Seroa lizable; . . . private Collection<Phone> phoneNumbers = n ew ArrayList<Phone>(); . . . @OneToMany public Collection<Phone> getPhoneNumbers() { return phoneNumbers; } . . . }

Pokud není specifikované žádné mapování, správce perzistence provede defóltní mapování založené na spojeném mapování tabulek probírané dříve. Příklad použití – rezervační systém na lodi:

V rezervačním systému Titan je více entit a kardinalit mezi entitami – viz obrázek.

Obr 5.1 Vazby mezi třídami rezeravčního systému 5.4 Jednosměrná relace many-to-one Entita Criuse má relaci Many-to-One s entitou Ship. Každá entita Cruise udržuje referenci na entitu Ship. Opačně to neplatí. Schéma relační databáze CREATE TABLE SHIP ( ID INT PRIMARY KEY NOT NULL, NAME CHAR(30), TONNAGE DECIMAL (8,2) )

• Tabulka CRUISE (plavba) udržuje odkaz na entitu Ship. CREATE TABLE CRUISE ( ID INT PRIMARY KEY NOT NULL, NAME CHAR(30), SHIP_ID INT )

Programový model many-to-one Relace many-to-one využívá anotace @javax.persistence.ManyToOne:

Ship

Criuse

Reservation

Cabin

Customer

1

*

1

*

*

*

*

*

1

*

Page 51: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

51

public @interface ManyToOne { Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default EAGER; boolean optional() default true; }

@Entity public class Cruise implements java.io.Serializable { private int id; private String name; private Ship ship; public Cruise() {} public Cruise(String name, Ship ship) { this.name = name; this.ship = ship; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName( ) { return name; } public void setName(String name) { this.name = n ame; } @ManyToOne @JoinColumn(name=“SHIP_ID) public Ship getShip() { return ship; } public void setShip(Ship ship) { this.ship = shi p; } }

Specifikace Java Persistence vyžaduje také neparametrický konstruktor. Anotace @JoinColumn specifikuje, že entitní tabulka Cruise má dodatečný sloupec nazvaný SHIP_ID, který je cizím klíčem k entitní tabulce Ship. @Entity public class Ship implements java.io.Serializable { private int id; private String name; private double tonnage; public Ship() {} public Ship(String name, double tonnage) { this.name = name; this.tonnage = tonnage; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = n ame; }

Page 52: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

52

public double getTonnage() { return tonnage; } public void setTonnage(double tonnage) { this.to nnage = tonnage; } }

Každá entita Cruise může mít referenci pouze na jednu entitu Ship. 5.5 Obousměrná relace one-to-many Příkladem takovéto relace může být v Titan Cruise systému, že každá entita Cruise udržuje kolekci referencí na všechny rezervace pasažérů vytvořených pro danou plavbu (Cruise) a každá rezervace udržuje jednoduchou referenci na tuto plavbu (Cruise). Schéma relační databáze CREATE TABLE RESERVATION ( ID INT PRIMARY KEY NOT NULL, AMOUNT_PAID DECIMAL (8,2), DATE_RESERVATION DATE, CRUISE_ID INT )

Zatímco tabulka RESERVATION obsahuje cizí klíč do tabulky CRUISE, tabulka CRUISE neudržuje cizí klíč zpět do tabulky RESERVATION. Správce perzistence může určit relaci mezi entitami CRUISE a RESERVATION dotazem v tabulce RESERVATION. Explicitní ukazatel z tabulky CRUISE do tabulky RESERVATION vyžadovaný není. Programový model @Entity public class Reservation implements java.io.Seriali zable { private int id; private Date date; private double amountPaid; private Cruise cruise; public Reservation() {} public Reservation(Cruise cruise) { this.cruise + cruise; } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } @Column(name=“DATE_RESERVED“) public Date getDate() { return date; } public void setDate(Date date) { this.date = dat e; } @Column(name=“AMOUNT_PAID“) public double getAmountPaid() { return amountPai d; } public void setAmountPaid(double amount) { amoun tPaid = amount; } @ManyToOne @JoinColumn(name="CRUISE_ID") public Cruise getCruise() { return cruise; } public void setCruise(Cruise cruise) { this.crui se = cruise; } }

Page 53: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

53

Je třaba přidat vlastnost relace založené na kolekci do třídy Cruise bean tak, aby mohla odkazovat na všechny Reservations, které byly proto vytvořeny: @Entity public class Cruise implements java.io.Seriali zable { . . . private Collection<Reservation> reservation s = new Ar rayList<Reservation>(); . . . @OneToMany(mappedBy="cruise") public Collection<Reservation> getReserva tions() { return reservations; } public void setReservations(Collection<Re servation> res) { this.reservations = res; } }

Vzájemná závislost mezi entitami Cruise a Reservation produkuje některé zajímavé výsledky. Stejně jako u obousměrné relace one-to-one musí i zde být jedna strana, která vlastní relaci (owning side). Java Persistence aktuálně vyžaduje, že strana many-to-one musí být vždy vlastníkem relace, což je v uvedeném příkladě entita Reservation. To znamená, že musíte vyvolat Reservation.setCruise() kdykoli, když přidáváte nebo odstraňujete rezervaci plavby. Pokud to neprovedete, v databázi se relace nezmění. Je třeba si uvědomit, že obě strany na sebe odkazují. V uvedeném příkladě rezervačního systému entity Reservation se nikdy nezmění jejich plavby (Cruises). Když chce zákazník rezervovat (zabukovat) jinou plavbu (cruise), musí zrušit starou rezervaci a vytvořit novou rezervaci (místo nastavení Reservation.setCruise(null)). entityManager.remove(reservation);

Defóltní mapování Podobně jako v předchozích případech defóltní mapování umožňuje vynechat specifikaci metadat @JoinColumn. @Entity public class Reservation implements java.io.Se rializable { . . . @ManyToOne public Cruise getCruise() { return cruise; } . . . }

Toto mapování vytváří sloupec cizího klíče pojmenovaný podle kombinujících názvů vlastnosti, kterou mapujete následovanou n znaky a jménem sloupce primárního klíče referenční tabulky. 5.6 Obousměrná relace many-to-many Příkladem této relace může být entita Reservation, která může odkazovat na mnoho entit Customer a každá entita Customer může mít několik entit Reservation.K této relaci je třeba vytvořit tabulku RESERVATION_CUSTOMER: CREATE TABLE RESERVATION_CUSTOMET ( RESERVATION_ID INT, CUSTOMER_ID INT )

Page 54: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

54

Tato relace vyžaduje vždy asociační tabulku normalizované relační databáze. Programový model Rozhraní many-to-many je definované s použitím anotace @javax.persistence.ManyToMany: public @interface ManyToMany { Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default LAZY; String mappedBy() default „“; }

@Entity public class Reservation implements java.io.Seriali zable; . . . private Set<Customer> customers = new HashSet<Cu stomer>(); . . . @ManyToMany @JoinTable(Name=”RESERVATION_CUSTOMER”, joinColumn={@JoinColumn(name=”RESER VATION_ID)}, inverseJoinColumn={@JoinColumn(name =”CUSTOMER_ID”)}) public Set<Customer> getCustomer() { return cust omers; } public void setCustomers(Set customers); . . . }

K modelování relace mezi entitami Customer a Reservation je třeba přidat do každé třídy relaci založené na kolekci (collection-based relationship) Datový typ množina (Set) obsahuje pouze jedinečné entity Customer, což je logický požadavek. Jako ve všech obousměrných relacích je třeba, aby i zde byla vlastnící strana (owning side), což je entita Reservation. Protože Reservation vlastní relaci, její třída bean definuje mapování @JoinTable. Atribut joinColumn identifikuje sloupec cizího klíče v tabulce RESERVATION_CUSTOMER, která odkazuje na tabulku RESERVATION. Atribut inverseJoinColumn identifikuje cizí klíč v tabulce RESERVATION_CUSTOMER, která odkazuje do tabulky CUSTOMER. Podobně jako v předešlých případech, pokud správce perzistence poskytuje prostředky pro vytváření auto schéma, není třeba specifikovat metadata @JoinTable. Mapování @ManyToMany vytvoří spojenou tabulku za vás. Třída Customer bean má následující strukturu: @Entity public class Customer implements java.io.Serializab le { . . . private Collection<Reservation> reservations = new ArrayList<Reser vation>(); . . . @ManyToMany(mappedBy=”customers”) public Collection<Reservation> getReservations() { return reservations; } public void setReservations(Collection<Reservati on> reservations) { this.reservations = reservations; } . . .

Page 55: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

55

Atribut mappedBy() identifikuje vlastnost ve třídě Reservation bean, která definuje relaci. To také identifikuje entita Customer jako inverzní strana relace. Přidání / zrušení rezervace k danému zákazníkovi se provede následovně: Reservation reservation = em.find(Reservation .class, id); reservation.getCustomers().remove(customer);

Protože rezervace je vlastnící strana relace, je třeba entitu Customer rušit pomocí vlastnosti (atributu) customers entity Reservation. Kdyby se místo toho odstranila reservation pomocí vlastnosti reservation entity Customer, žádná aktualizace by se v databázi neprovedla, protože entita Customer je inverzní stranou relace. 5.7 Jednosměrná relace many-to-many Příkladem takové relace je v rezervačním systému Titan přiřazení každé entity Reservation k entitě Cabin. To dovoluje entitě Customer rezervovat si konkrétní entitu Cabin na lodi (Ship). V tomto případě může být každá rezervace učiněna pro více než jednu kabinu, protože každá rezervace může být pro více než jednoho zákazníka. Například rodina si může rezervovat dvě oddělené kabiny, jednu pro dospělé a druhou pro děti. Zatímco entita Reservation musí sledovat entity Cabins, které entity Reservation rezervují, entity Cabins nemusí sledovat entity Reservations vytvořené pro všechny plavby. Entity Reservations odkazují na kolekci entit Cabins, ale entity Cabins neudržují odkazy zpět do Reservations. Relační databázové schéma CREATE TABLE CABIN ( ID INT PRIMARY KEY NOT NULL, SHIP_ID INT, NAME CHAR(20), DECK_LEVEL INT, BED_COUNT INT )

Tabulka CABIN udržuje cizí klíč do tabulky SHIP. K vyřešení jednosměrné relace many-to-many mezi tabulkami RESERVATION a CABIN je potřebná tabulka CABIN_RESERVATION: CREATE TABLE CABIN_RESERVATION ( RESERVATION_ID INT, CABIN_ID INT )

Programový model K modelování této relace je potřebné přidat relační pole založené na kolekci typu Cabin do Reservation: @Entity public class Reservation implements java.io.Seriali zable { . . . @ManyToMany @JoinTable(name=”CABIN_RESERVATION”, joinColumns={@JoinColumn(name=”RE SERVATION_ID)}, inverseJoinColumns={@JoinColumn(n ame=”CABIN_ID”)}) public Set<Cabin> getCabin(Set<Cabin> cabins) {

Page 56: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

56

returns cabins; } public void setCabins(Set<Cabin> cabins) { this.cabin = cabin; } . . . }

Dále je třeba definovat bean Cabin, který neudržuje žádnou relaci zpět do Reservation. @Entity public class Cabin implements java.io.Serializable { private int id; private String name; private int bedCount; private int deckLevel; private Ship ship; @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = n ame; } @Column(name=“BED_COUNT“) public int getBedCount() { return bedCount; } public void setBedCount(int count) { this.bedCou nt = count; } @Column(name=“DECK_LEVEL“) public int getDeckLevel() { return deckLevel; } public void setDeckLevel(int level) { this.deckL evel = level; } @ManyToOne @JoinColumn(name="SHIP_ID") public Ship getShip() { return ship; } public void setShip(Ship ship) { this.ship = shi p; } }

Odpojené entity a FetchType (Detached Entities and FetchType) Entity mohou být připojeny nebo odpojeny od perzistentního kontextu, viz předchozí kapitoly. Když se instance entity odpojí od perzistentního kontextu, její stav (datové atributy) nemůže být plně inicializovaný, protože některé perzistentní vlastnosti, nebo relace mohou být označeny jako „lazily loaded“ v mapování metadat. Každá relační anotace má atribut fetch(), který určuje, zda relační vlastnost je nahrávána (loaded) v případě dotazu na entitu. Pokud je atribut fetch() nastaven na FetchType.LAZY, pak relace není inicializovaná, pokud není traverzovaná vašim kódem: Customer customer = entityManager.find(Custome r.class, id); customer.getPhoneNumbers().size();

Vyvolání metody size() způsobí nahrání relace z databáze. Je důležité poznamenat, že inicializace lazy nenastane dříve, dokud entitní bean není řízen perzistentním kontextem. Je-li entitní bean odpojen, není jasné jakou činnost správce perzistence provede. Často proto dochází k vyhození chyby. Cruise detachedCruise = . . .;

Page 57: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

57

try { int numberReservations = detachedCruise.getR eservations().size(); } catch(SomeVendorLazyInitializationExcepti on ex) { }

Aplikace obdržela instanci odpojené entity Cruise a pokouší se zpřístupnit rezervace @OneToMany, ale protože je fetch() LAZY bude vyhozena výjimka. Problém lazy inicializace můžemě překonat dvěma způsoby: • nejzřejmější způsob je navigovat potřebnou relaci v době, kdy je entitní instance řízena

(připojena) perzistentním kontextem, • druhý způsob je provést fetch EAGER v době dotazu entity (podrobněji v dotazech EJB

QL) 5.8 Kaskádování Když entitní manager vykonává operaci na instanci entitního beanu, může být také potřeba vykonání stejné operace automaticky na dalších relacích, které má daná entita. Např. pokud je nová entita Customer s novou adresou a telefonním číslem vytvářena perzistentní, vše co musíte udělat je zkompletovat objekt a entitní manager vytvoří entitu customer a její další relační entity a vše udělá perzistentní: Customer cust = new Customer(); cust.setAddress(new Address()); cust.getPhoneNumbers().add(new Phone()); // vytvo ří se všechny, jedním vyvoláním entitniho Managera entityManager.persist(cust);

V Java Persistence kaskádování zahrnuje následující operace: persist(), merge(), remove(), refresh(). K jejich nastavení se využívá CascadeType: public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH }

Hodnota ALL reprezentuje všechny kaskádní operace. Podívejme se na jednosměrnou relaci one-to-one mezi Customer/Address s kaskádním typem REMOVE a PERSIST: @Entity public class Customer implements java.io.Serializab le { . . . private Address address; @OneToOne(cascade={CascadeType.PERSIST, CascadeT ype.REMOVE}) @JoinColumn(name="ADDRESS_ID") public Address getAddress() { return address; } public void setAddress(Address address) { this.a ddress = address; } }

Kdykoli je aplikována metoda persist() na Customer a s ním asociovanou entitu Address je také entita Address vytvořena perzistentní. Je-li použita operace remove() na entitu Customer, je s ním současně odstraněna z databáze i entita Address.

Page 58: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

58

PERSIST má co dočinění s vytvářením entit v rámci databáze. Je-li použita, automaticky se s danou entitou uloží do databáze i další její relační entity. Jinak by se např. na entitu Address musela metoda persist() vyvolat samostatně: entityManager.persist(address);

MERGE je hlavně využitelná při vkládání a aktualizaci. Jak je zřejmé, objekty mohou být odpojeny od perzistentního řízení, serializovány, zaslány vzdálenému klientovi, aktualizace jsou provedeny lokálně vzdáleným klientem, pak je objekt zaslán zpět serveru a změny by měly být promítnuty zpět do databáze. Merging je o synchronizaci stavů odpojeného objektu zpět do perzistentní paměti. MERGE je podobné PERSIST. Je-li nastavena kaskádní politika, pak je MERGE zavoláno pouze na jeden objekt: cust.setName(“William”); cust.getAddress().setCity(„Boston“); entityManager.merge(cust);

Další zajímavá věc na MERGE je, že když přidáte nové entity do relace, která ještě nebyla vytvořena v databázi, pak se vše provede vykonáním merge(): Phone phone = new Phone(); phone.setNumber(“617-666-456”); cust.getPhoneNumber().add(phone); entityManager.merge(cust);

REMOVE je zřejmý. Pokud se ruší entita Customer, bude zrušena také její entita Address. Customer cust = entityManager.find(Customer.cl ass, 1); entityManager.remove(cust);

REFRESH neaktualizuje databázi s hodnotami instanci, ale aktualizuje stavy instancí z databáze. Tyto změny v databázi byly provedeny samozřejmě jinou transakcí. Např.: Customer cust = . . .; entityManager.refresh(cust); // address bude aktualizovaná také

All je kombinací všech předešlých politik a je využívána kvůli jednoduchosti. Samozřejmě kaskádování není vhodné pro všechny relace. Např. nebudete chtít odstranit entity Cruise nebo Customer při odstraňování entity Reservation. Nastane problém, protože „život“ těchto entit je delší než život entity Reservation.

Technologie EJB rozlišuje celkem sedm typů entitních relací. Všechny tyto typy jsou popsány a vysvětleny na příkladech. Tato část také obsahuje kaskádování a jeho použití.

Page 59: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

59

6 Dědičnost entit a zpětná volání posluchači

V této kapitole se dozvíte: • jaké jsou možnosti převodu hierarchické struktury objektů do relačních tabulek, • jak probíhají zpětná voláni.

Specifikace Java Persistence poskytuje tři různé způsoby, jak mapovat hierarchii dědičnosti do relační databáze: 1. Jedna tabulka pro hierarchii tříd Jedna tabulka bude mít všechny vlastnosti (atributy) každé třídy v hierarchii. 2. Tabulka pro konkrétní třídu Každá třída bude mít pro ni deklarovanou tabulku, se všemi jejími vlastnostmi a vlastnostmi její nadtřídy mapované do této tabulky. 3. Tabulka pro podtřídu Každá třída bude mít svoji vlastní tabulku. Každá tabulka bude mít vlastnosti, které jsou definované v té konkrétní třídě. Tyto tabulky nemají žádné vlastnosti svých nadtříd nebo podtříd.

Obr. 6.1 Diagram tříd UML - dědičnost 6.1 Jedna tabulka pro hierarchii tříd Do jednoduché tabulky jsou mapovány všechny třídy. To znamená, že datové atributy tříd Person, Customer a Empoloyee jsou jako vlastnosti v jedné tabulce: create table PERSON_HIERARCHY ( id integer primary key not null, firtsName varchat(255), lastName varchar(255), street varchar(255), city varchar(255), state varchar(255), zip vatchar(255), employeeId integer, DISCRIMINATOR varchar(31) not null, //ident if. typu uložené entity );

Speciální sloupec DISCRIMINATOR identifikuje typ entity, který je uložen v dané tabulce. Anotace pro strategii hierarchie @Entity @Table(name="PERSON_HIERARCHY") @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="DISCRIMINATOR", discriminatorType=Discriminato rType.STRING) @DiscriminatorValue("PERSON") public class Person implements java.io.Serializable

Person

-firstName-lastName

Customer

-street-city-state-zip

Employee

-employeeId

Page 60: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

60

{ private int id; private String firstName; private String lastName; @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String first) { this.fi rstName = first; } public String getLastName() { return lastName; } public void setLastName(String last) { this.last Name = last; } }

Pro definování perzistentní strategie pro relaci dědičnosti se používá anotace @javax.persistance.Inheritance: package javax.persistence; @Target(TYPE) @Retention(RUNTIME) public @interface Inheritance { InheritanceType strategy() default SINGLE_ TABLE; } public enum InteritanceType { SINGLE_TABLE, JOINED, TABLE_PER_CLASS }

Atribut strategy() definuje použité mapování instance. Anotace @Inheritance musí být umístěna v kořenu hierarchie třídy, pokud neměníte strategii mapování v podtřídě. package javax.persistence; @Target(TYPE) @Retention(RUNTIME) public @interface DiscriminationColumn String name() deafult “DTYPE”; DiscriminationType discriminationType() de fault STRING; String columnDefinition() default “”; int length() default 10; }

Sloupec Discriminator určuje instance, které třídy tabulka reprezentuje. Atribut name() určuje jméno sloupce a dicsriminatorType() specifikuje typ diskriminačního sloupce. Typem může být STRING, CHAR nebo INTEGER. Implicitní je STRING. package javax.persistence; @Target(TYPE) @Retention(RUNTIME) public @interface DiscriminatorValue { String value(); }

Anotace @javax.persistence.DiscriminatorValue definuje, jakou hodnotu bude mít diskriminační sloupec pro řádek, ve kterém je uložena instance třídy Person. Pokud zůstane atribut nedefinovaný, správce perzistence vygeneruje hodnotu automaticky. Jedinými metadaty specifikujícími dědičnost je hodnota diskriminátoru, pokud chcete jinou než defóltní hodnotu.

Page 61: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

61

@Entity @DiscriminatorValue("CUST") public class Customer extends Person { private String street; private String city; private String state; private String zip; public String getStreet() { return street; } public void setStreet(String street) { this.stre et = street; } . . . } // použití defóltní hodnoty diskriminátoru @Entity public class Employee extends Customer { private int employeeId; public int getEmployeeId() { return employeeId; } public void setEmployeeId(int id) { employeeId = id; } }

V uvedeném příkladu entita Customer nastavuje hodnotu sloupce diskriminátor na CUST s využitím anotace @DiscriminatorValue. U entity Employee je hodnota sloupce diskriminátor nastavena defóltně na Employee. Výhody • Mapování SINGLE_TABLE je nejjednodušší na implementaci a provádění. • Pracuje se pouze s jednou tabulkou. • Správce perzistence nemusí provádět žádné složité spojování sjednocení nebo výběry,

když nahrává entity, nebo když traverzuje polymorfické relace, protože všechna data jsou uložena v jedné tabulce.

Nevýhody • Jedna velká nevýhoda tohoto přístupu je, že všechny sloupce vlastností podtříd musí být

nastavitelné na null. • Pokud potřebujete nějakou vlastnost nastavit na NOT NULL je to problém, protože to

nejde. • Protože sloupce vlastností podtříd nemusí být využity, není uvedená strategie

normalizovatelná. 6.2 Tabulka pro konkrétní třídu Každá tabulka má své vlastnosti a všechny vlastnosti svých nadtříd. create table Person ( id integer primary key not null, firstName varchar(255), lastName varchar(255) ); create table Customer ( id integer primary key not null, firstName varchar(255), lastName varchar(255) street varchar(255),

Page 62: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

62

city varchar(255), state varchar(255), zip varchar(255) ); create table Employee ( id integer primary key not null, firstName varchar(255), lastName varchar(255) street varchar(255), city varchar(255), state varchar(255), zip varchar(255) employeeId integer );

Jedna hlavní odlišnost mezi danou strategií a strategií SINGLE_TABLE je, že není potřebný žádný sloupec diskriminátor v databázovém schématu. Mapování této strategie s využitím anotace: @Entity @Inheritance(strategy=InheritanceType.TABLE_PER_CLA SS) public class Person implements java.io.Serializable { . . . } @Entity public class Customer extends Person { . . . } @Entity public class Employee extends Customer { . . . }

Metadata jsou uvedena pouze ve třídě Person. Výhody • Výhoda tohoto přístupu oproti SINGLE_TABLE je v tom, že je možné definovat omezení

(NOT NULL) na vlastnostech (atributech) podtříd. Nevýhody • Tato strategie není normalizovaná, protože má nadbytečné (redundantní) sloupce. • Tato strategie může být implementovaná tak, že kontejner používá vícenásobné dotazy při

nahrávání entit nebo polymorfických relací. • To je pro kontejner hodně náročné. Jiný způsob implementace je, že kontejner používá

SQL UNION operace. • Je to rychlejší než předchozí implementace vícenásobných výběrů. • Problémem tohoto způsobu implementace může dále být, že některé databáze nepodporují

uvedený rys SQL.

Page 63: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

63

6.3 Tabulka pro podtřídu V této strategii mapování má každá podtřída svoji vlastní tabulku, ale tato tabulka obsahuje pouze vlastnosti, které jsou definované v konkrétní třídě. Stručně řečeno, tato strategie je podobná strategii TABLE_PER_CLASS s výjimkou toho, že schéma je normalizované. Také někdy nese označení JOINED strategie: create table Person ( id integer primary key not null, first name varchar(255), lastName varchar(255) ); create table Customer ( id integer primary key not null, street varchar(255), city varchar(255), state varchar(255), zip varchar(255) ); create table Employee ( EMP_PK integer primary key not null, employeeId integer );

Když správce perzistence nahrává entitu (load), která je podtřídou, nebo traverzuje polymorfické relace, používá SQL join operaci na všechny tabulky hierarchie. Při mapování musí být v každé tabulce sloupec, který se využívá při spojování každé tabulky. V našem příkladě tabulky EMPLOYEE, CUSTOMER a PERSON sdílí hodnotu stejného primárního klíče. Anotace pro mapování @Entity @Inheritance(strategy=InheritanceType.JOINED) public class Person implements java.io.Serializable { . . . } @Entity @DiscriminatorValue("CUST") public class Customer extends Person { . . . } @Entity @PrimaryKeyJoinColumn(name="EMP_PK") public class Employee extends Customer { . . . }

Správce perzistence potřebuje vědět, který sloupec v každé tabulce bude použit pro spojování, při nahrávání entity. K tomu se používá anotace @javax.persistence.PrimaryKeyJoinColumn: package javax.persistence; @Target({TYPE, METHOD< FIELD}) public @interfacfe PrimaryKeyJoinColumn String name() default “”; String referencedColumnName() default “”; String columnDefinition() default “”; )

Page 64: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

64

Atribut name() odkazuje na sloupec, kterým budete provádět operaci join. Je obsažen v dané tabulce. Defóltně odkazuje na primární klíč nadtřídy. Sloupec referencedColumnName() bude použit k provádění operace join z tabulky nadtřídy. Může to být libovolný sloupec tabulky nadtřídy, standardně je to primární klíč. Jsou-li jména primárních klíčů mezi danou třídou a podtřídou identická, pak není třeba specifikovat anotaci (Customer, Person). Pro složený klíč se využije anotace @javax.persistence.PrimaryKeyJoinColumns: package javax.persistence; @Target({TYPE, METHOD, FIELD}) public @interface PrimaryKeyJoinColumns { @PrimaryKeyJoinColumns[] values(); }

Výhody • Není tak rychlé jako strategie SIMPLE_TABLE, je ale možné definovat omezení NOT

NULL a model je normalizovatelný. • Tato strategie je lepší než strategie TABLE_PER_CLASS ze dvou důvodů:

1. Model relační databáze se dá kompletně normalizovat. 2. Pracuje lépe, pokud databáze nepodporuje SQL UNIONS operace.

• Nepracuje tak rychle jako strategie SINGLE_TABLE. 6.4 Zpětná volání entity a posluchači (Entity Callbacks and Listeners) Když EntityManager vykonává metody jako např. persist(), merge(), find() a remove(), nebo libovolný dotaz EJB QL, je spuštěna předdefinovaná množina událostí životního cyklu. Např. metoda persist() spouští databázový inserts, metoda merge() spouští updaty v databázi apod. Někdy je velmi užitečné, aby třída entitního beanu byla informovaná o tom, že nastaly uvedené události (spustily se dodatečné metody). Specifikace Javy Persistence dovoluje vytvořit metody zpětného volání (callback methods) ve třídě beanu tak, aby entitní instance byly informovány, že tyto události nastaly. Je možné také registrovat odděleného posluchače, který bude očekávat ty stejné události. Události zpětného volání (Callback Events) Daná anotace reprezentuje každou fázi životního cyklu entity: • @javax.persistence.PrePersist • @javax.persistence.Postersist • @javax.persistence.PostLoad • @javax.persistence.PreUpdate • @javax.persistence.PostUpdate • @javax.persistence.PreRemove • @javax.persistence.PostRemove Událost @PrePersist nastane ihned, když entityManager vyvolá metodu persist(), nebo kdykoli entitní instance je naplánovaná na vložení do databáze (jako kaskádní merge). Podobně událost @PostPersist není spuštěna, dokud není dokončeno vložení do databáze.

Page 65: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

65

Zpětné volání v entitních třídách (Callback on Entity Classes) Instance entitního beanu může být registrovaná pro metodu zpětného volání anotací třídy beanu pomocí metody bez argumentů, která vrací void a vyhazuje nekontrolovanou výjimku. @Entity public class Cabin { . . . @PostPersist void afterInsert() { . . . } @PostLoad void afterLoading() { . . . } }

Nastane-li odpovídající událost, entity manager vyvolá odpovídající anotovanou metodu. Posluchači entity (Entity Listeners) Posluchači entit jsou třídy, které mohou genericky zachytit události zpětného volání. Nejsou to entitní třídy jako takové, ale mohou být připojeny ke třídě entit prostřednictvím vazby anotace nebo XML. Ve třídě entitního posluchače můžete označit metody, které zachytí konkrétní událost životního cyklu. Tyto metody vrací void a mají jeden Object jako parametr, což je instance entity, na které nastane událost. public class TitanAuditLogger { @PostPersist void postInsert(Object entity) { System.out.println(“Inserted entity: “ + entity.getClass().getName()); } @PostLoad void postLoad(Object entity) { System.out.println(“Loaded entity: “ + entity.getClass().getName()); } }

Třída posluchače entity musí mít veřejný konstruktor bez argumentů. Ten může být aplikován na entitní třídu s využitím anotace @javax.persistence.EntityListeners: package javax.persistence; @Target(Type) @Retention(RUNTIME) public @interface EntityListeners { Class[ ] value(); }

V entitní třídě je možné specifikovat jeden nebo více posluchačů: @Entity @EntityListeners({TitanAuditLogger.class, Enti tyJmxNotifier.class}) public class Cabin { . . . @PostPersist void afterInsert() { . . . } @PostLoad void afterLoading() { . . . } }

Použitím anotace @EntityListeners na entitu Cabin způsobí že, jakákoli metoda zpětného volání uvnitř entitních posluchačů bude vyvolána, kdykoli instance entity Cabin bude v interakci s perzistentním kontextem.

Page 66: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

66

Anotace zabezpečí vyvolání metod zpětného volání kdykoli instance třídy Cabin bude v interakci s perzistentním kontextem. Tvar XML: <entity class="com.titan.domain.Cabin"> <entity-listeners> <entity-listener class="com.titan.listeners .TitanAuditLogger"> </entity-listener>" <entity-listener class="com.titan.listeners .EntityJmxNotifier"> <pre-persist name="beforeInsert"/> <post-load name="afterLoading"/> </entity-listener> </entity-listeners> </entity>

Pořadí vykonávání entitních posluchačů je pořadí, ve kterém byly v anotace @EntityListeners nebo v XML mapovacím souboru. 1 EntityManager em = factory.createEntityManager() ; 2 em.getTransaction().begin(); 3 4 Cabin cabin = new Cabin(); 5 em.persist(cabin); 6 7 Cabin anotherCabin = em.find(Cabin.class, 5); 8 9 em.getTransaction().commit();

Třída EntityJmxNotifier se zajímá o metodu zpětného volání <pre-persist>. Proto je metoda EntityJmxNotifier.beforeInsert() vykonána hned při vyvolání metody em.persist(cabin) v řádku 5. V řádku 7 jsou vyvolány metody v následujícím pořadí: TitanAuditLogger.postLoad() EntityJmxNotifier.afterLoading() Cabin.afterLoading() po té co je vykonána metoda EntityManager.find(). Správce perzistence zdrží vkládání entity cabin dokud není transakce ukončena (commit). Je možné specifikovat množinu defóltních entitních posluchačů, kteří jsou aplikováni na každou entitní třídu v perzistentní jednotce s využitím elementu <entity-listeners> . Pokud potřebujete vypnout defóltní entitní posluchače na konkrétní entitě, použijete k tomu anotaci @javax.persistence.ExcludeDefaultListeners: @Entity @ExcludeDefaultListeners public class Cabin { . . . }

Page 67: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

67

Dědičnost hraje velmi významnou roli v objektově orientovaném paradigmatu. Kapitola o dědičnosti popisuje a vysvětluje tři základní přístupy, jak hierarchii dědičnosti převést do formy relačních tabulek, které využívá technologie relačních databází. Do této kapitoly jsou také zahrnuty zpětná volání (callback methods) a posluchači, kteří pomáhají tento postup realizovat.

Page 68: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

68

7 Dotazy a EJB QL

V této kapitole se dozvíte: • jak se vytváří dotazy a co poskytuje Query API, • jak se provádí stránkování výsledků, • jak se využívá EJB QL. EJB QL je deklarativní dotazovací jazyk, podobný SQL pro relační databáze, ale je uzpůsobený pro práci s javovskými objekty. Při prováděním dotazu EJB QL používá správce perzistence informace získané z mapovaných metadat a převede dotaz na nativní SQL dotaz. EJB QL a nativní dotazy SQL jsou prováděny prostřednictvím rozhraní javax.persistence.Query. Toto dotazovací API dodává metody pro stránkování result set, stejně jako možnosti předávání javovských parametrů do dotazu. Dotazy mohou být předdefinovány pomocí anotací nebo XML, nebo vytvořeny dynamicky za běhu prostřednictvím API EntityManageru. 7.1 Query API Ve specifikaci Java Persistence je rozsáhlé rozhraní Javy, které můžete získat za běhu od entitního manažera. package javax.persistence; public interface Query { public List getResultList(); public Object getSingleResult(); public int executeUpdate(); public Query setMaxResults(int maxResult); public Query setFirstResult(int startPosition); public Query setHint(String hintName, Object val ue); public Query setParameter(String name, Object va lue); public Query setParameter(String name, Date valu e, TemporalType temporalType); public Query setParameters(String name, Calendar value, TemporalType temporalType); public Query setParameter(int position, Object v alue); public Query setParameter(int position, Date val ue, TemporalType temporalType); public Query setParameter(int position, Calendar value, TemporalType temporalType); public Query setFlushMode(FlushModeType flushMod e); }

Dotazy jsou vytvářeny s použitím těchto metod EntityManageru: package javax.persistence; public interface EntityManager { public Query createQuery(String ejbqlString); public Query createNamedQuery(String name); public Query createNativeQuery(String sqlString) ; public Query createNativeQuery(String sqlString, Class resultClass);

Page 69: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

69

public Query createNativeQuery(String sqlString, String res ultSetMapping); }

Využití EntityManageru a metody createQuery(), která vytváří dotaz dynamicky za běhu: try { Query query = entityManager.createQuery( “form Customer c whwre c.firstNa me=‘Bill‘ and c.lastName=‘Burke ‘”); Customer cust = (Customer)query.getSingleResult( ); } catch(EntityNotFoundException notFound) { } catch(NonUniqueResultException nonUnique) { }

Uvedený dotaz je pro entitu jednoho jedinečného (single, unique) zákazníka jménem Bill Burke. Dotaz je vykonán vyvoláním metody getSingleResult(). Metoda předpokládá návrat pouze jednoho výsledku. V případě nenalezení entity je vyhozena výjimka EntityNotFoundException a v případě nalezení více entit je vyhozena výjimka NonUniqueResultException. V případě, že bude výsledkem dotazu více entit, např. bude existovat několik osob se jménem Bill Burke, je možné využít metodu getResultList(): Query query = entityManager.createQuery( “from Customer c where c.firstNa me=’Bill’ and c.la stName=’Burke’); java.util.List bills = query. getResultList();

Velmi podobně jako java.sql.PreparedStatement v JDBC, dovoluje EJB QL specifikovat parametry v query deklaracích, takže je možné query (dotaz) použít znova a několikrát na různé množiny parametrů. Modifikujeme předchozí příklad: public List findByName(String first, String last) { Query query = entityManager.createQuery( “from Customer c where c.firstName=:first and c.lastName=: last); query.setParameter(“first, first); query.setParameter(“last”, last); return query.getResultList(); }

Znak ‘:‘ následovaný jménem parametru se používá v příkazech EJB QL k identifikaci jména parametru. Místo řetězcových jmenných parametrů umožňuje metoda setParameter() také číselné parametry pozice. public List findByName(String first, String last) { Query query = entityManager.createQuery( “from Customer c where c.firstName=?1 and c.lastName=? 2); query.setParameter(1, first); query.setParameter(2, last); return query.getResultList();

Page 70: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

70

}

Znak ‘?‘ je použit místo znaku ‘:‘, který je použit se jmenným parametrem. Parametry datumu se používají, pokud do dotazu potřebujeme zapracovat parametry java.util.Calendar nebo java.util.Date. Pak použijete speciální metody setParameter(): package enum TemporalType { DATE, //java.sql.Date TIME, //java.sql.Time TIMESTAMP //java.sql.Timestamp } public interafce Query { Query setParameter(String name, java.util.Date v alue, TemporalType temporalType); Query setParameter(String name, java.util.Calend ar value, TemporalType temporalType); Query setParameter(int position, java.util.Date value, TemporalType tem poralType); Query setParameter(int position, java.util.Date value, TemporalType tempo ralType); }

Protože parametry mohou představovat různé věci v daném čase, je třeba říci Query objektu, jak by měl použít tyto parametry. To řeší parametr javax.persistence.TemporalType. Ten říká rozhraní Query, který databázový typ použít při převodu java.util.Date nebo java.util.Calendar na nativní typ SQL. 7.2 Stránkování výsledků (Paging Results) API Query má dvě vestavěné funkce pro řešení problému stránkování příliš rozsáhlých výsledků. Jedná se o setMaxResults() a setFirstResult(): public List getCustomers(int max, int index) { Query query = entityManager.createQuery(“fro m Customer c”); return query.setMaxResults(max). setFirstResult(index). getResultList(); }

Metoda getCustomers() provede dotaz a získá všechny zákazníky databáze. Metoda setMaxResults() omezuje (limituje) počet předaných zákazníků. Metoda setFirstResult() říká query, od které pozice chceme dotazované zákazníky. Je-li max-result nastaven na 3 a first-result na 5, výsledkem budou zákazníci 5, 6, 7. Fragment kódu při práci s databází: List results; int first = 0; int max = 10; do {

Page 71: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

71

results = getCustomers(max, first); Iteratir it = results.iterator(); while(it.hasNext()) { Customer c = (Customer)it.getnext(); System.out.println(c.getFirstName() + “ “ + c.getLastName()); } entityManager.clear(); first = first + results.getSize(); } while (results.size() > 0);

Metoda entityManager.clear() způsobí odpojení (detach) všech zákazníků (customers), které jsme vypisovali a jejich ponechání garbage collectoru. FlushMode Problémem je změna flushModu entitním manažerem během provádění dotazu. Proto rozhraní Query poskytuje metodu setFlushMode(), pro tento účel: Query query = manager.createQuery(“from Custom er c”); query.setFlushMode(FlushModeType.COMMIT);

V uvedeném příkladě říkáme správci perzistence, aby nedělal žádné automatické ukládání (flushing) před dokončením dotazu. Doporučeno: FlushModeType.AUTO 7.3 EJB QL EJB QL je vyjádřeno v termínech schéma abstraktní perzistence entity: jméno abstraktního schéma, základní vlastnosti a relační vlastnosti. • EJB QL používá:

• jména abstraktního schématu k identifikaci beanu, • základní vlastnosti ke specifikaci hodnot a • relační vlastnosti pro navigaci relacemi.

EJB QL - Abstraktní schéma jmen Abstraktní schéma jmen může být definováno prostřednictvím metadat, nebo může mít defóltní specifikační hodnotu. Tato defóltní hodnota je nekvalifikované jméno entity, pokud atribut name() není specifikován v anotaci. V následujícím fragmentu není specifikován atribut @Entity.name(), proto bude použito jméno Customer, jako odkaz na danou entitu ve voláních EJB QL: package com.titan.domain; @Entity public class Customer { . . .} entityManager.createQuery(“SELECT c FROM Customer A S c”);

Zde je specifikované jméno Cust, na které je možné se odkazovat v dotazech EJB QL: package com.titan.domain; @Entity(name=”Cust”) public class Customer { . . . } entityManager.createQuery(“SELECT c FROM Cust AS c” );

Page 72: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

72

EJB QL - Jednoduché dotazy Takový dotaz nemá klauzuli WHERE a má pouze typ abstraktního schématu. SELECT OBJECT( c ) FROM Customer AS c

Výraz je podobný SQL které dovoluje, aby identifikátor byl asociovaný s tabulkou. Identifikátory nemohou být stejné jako existující jména hodnot abstraktního schématu. Navíc identifikátory proměnných jmen nerozlišují malá a velká písmena. Následující výraz je neplatný, protože Customer je jméno abstraktního schéma EJB Customer: SELECT OBJECT( customer ) FROM Customer AS cus tomer

Klauzule SELECT určuje typ libovolné hodnoty, která je vrácena. Operátor OBJECT() je volitelný a je to víceméně požadavek předchozí verze EJB 2.1. V následujících dotazech bude využito struktury tříd podle obr. 7.1 Obr. 7.1 Struktura tříd použita v dotazech Klauzule SELECT dovolují v EJB QL vrátit libovolný počet základních nebo relačních vlastností. Příklad na navrácení jmen a příjmení všech pasažérů na Titan plavbě: SELECT c.firstName, c.lastName FROM Customer A S c

Jména perzistentních vlastností jsou identifikována přístupovým typem třídy entitního beanu bez ohledu na to, zda jste použili anotaci mapování na metody get nebo set.

Ship

Cruise

Reservation

Cabin

Customer

Address

CreditCard

Phone

1*

1*

1

*

*

*

* *

1

1

1 11

*

Page 73: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

73

Pokud použijete get nebo set metody ke specifikaci vašich perzistentních vlastností, část get je jména metody je odstraněna a následuje malé písmeno: @Entity public class Customer implements java.io.Serializab le { private int id; private String first; private String last; @Id public int getId() { return id; } public String getFirstName() { return first; } public String getLastName() { return last; }

Klauzule SELECT bude mít následující tvar: SELECT c.firstName, c.lastName FROM Customer A S c

Pokud nejsou deklarovánu get a set metody, přistupuje se k vlastnostem přímo pomocí jmen vlastností. Pokud dotaz vrátí více než jednu položku, je třeba použít metodu Query.getResultList(). Výsledek je agregován do pole objektů (Object[]) v List: Query query = manager.createQuery( “SELECT c.firstName, c.lastName FRO M customer AS c”); List results = query.getResultList(); Iterator it = result.iterator(); while(it.hasNext()) { Object[] result = (Object[])it.next(); String first = (String)result[0]; String last = (String)result[1]); }

Je možné také použít tzv. jedno hodnotovou relaci v prostém příkazu selekce např. při získání kreditní karty zákazníka: SELECT c.creditCard FROM Customer c

EJB QL využije navigace viz obrázek. Podobně se můžeme dostat k položce city v Address: SELECT c.address.city FROM Customer AS c

Je také možná následující cesta dotazu, ovšem za předpokladu navigace: Customer->CreditCard->CreditCompany->Address: SELECT c.creditCard.creditCompany.address.city FROM Customer AS c

Navigaci cesty není možné provádět za položky perzistence (perzistence properties). Třída je uvedena jako datový atribut jiné třídy.

Page 74: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

74

EJB QL - Výrazy konstruktoru Jedním z nejmocnějších rysů EJB QL je schopnost specifikovat konstruktor uvnitř klauzule SELECT, který může alokovat obyčejné javovské objekty (ne entity) a předat specifikované sloupce entity do konstruktoru. Agregace jméno a příjmení z entity Customer do javovského objektu Name: public class Name { private String first; private String last; public Name(String first, String last) { this.first = first; this.last = last; } public String getFirst() { return first; } public String getLast() {return last; } }

Nyní je možné vrátit dotazem seznam objektů třídy Name, místo pouhého seznamu řetězců: SELECT new com.titan.domain.Name(c.firstName, c.lastName) FRO M Customer c

Objekt Query bude automaticky alokovat instanci třídy Name pro každý řádek databáze. EJB QL - Operátory IN a INNER JOIN Mnoho relací mezi entitními beany je založeno na kolekcích. Je důležité zpřístupnit a vybrat tyto relace. Není možné vybrat prvek přímo z relace založené na kolekci. Tuto činnost zvládne operátor IN: SELECT r FROM Customer AS c, IN(c.reservations) r

Operátor IN přiřadí jednotlivé prvky ve vlastnosti rezervace do identifikátoru r. Pomocí identifikátoru, který reprezentuje jednotlivé prvky kolekce, se na ně můžeme odkázat přímo a dokonce je vybrat v příkazu EJB QL. Příkaz vybere každou plavbu (cruise), kterou má zákazník rezervovanou: SELECT r.cruise FROM CUSTOMER AS c, IN( c.reservations ) r

Předchozí dotaz může být také vyjádřen pomocí INNER JOIN: SELECT r.cruise FROM Customer c INNER JOIN c.r eservations r

Operátor INNER JOIN je intuitivním operátorem hlavně pro vývojáře z relačního světa. Příklad speciálnějších výběrů, které spíše demonstrují možnosti uváděných operátorů: SELECT cbn.ship FROM Customer AS c, IN ( c.reservations ) r, IN( r.cabin ) cbn

Alternativně:

Page 75: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

75

SELECT cbn.ship FROM Customer c INNER JOINM c.reservations r INNER JOIN r.cabins cbn

Tyto dotazy vyberou všechny lodě, na které má zákazník rezervace. EJB QL - LEFT JOIN Tato syntaxe umožňuje procházet množinu entit, kde hledané hodnoty příkazu join nemusí existovat. Např. je třeba vypsat všechna jména zákazníků a všechna jejich telefonní čísla. Některý zákazník může mít více telefonních čísel, jiný žádné. SELECT c.firstName, c.lastName, p.number FROM Customer c LEFT JOIN c.phoneNumbers p

EJB QL - Fetch Joins Tato syntaxe dovoluje znova nahrát relace entity, i když vlastnost relace má nastavený FetchType na LAZY. Např. deklarujeme relaci one-to-many mezi entitou Customer a entitou Phone: @OneToMany(fetch=FetchType.LAZY); public Collection<Phone> getPhones() {return p hones; }

Když chceme získat informace o všech zákaznících, nejdříve se dotážeme na všechny zákazníky a pak budeme traverzovat metodou getPhones() jejich telefonní čísla:

1 Query query = manager.createQuery(“SELECT c FROM Customer c”); 2 List results = query.getResultList(); 3 Iterator it = results.iterator(); 4 while (it.hasNext()) { 5 Customer c = (Customer)it.next(); 6 System.out.print(c.getFirstName()+” “+ c.getLa stName()); 7 for (Phone p : c.getPhoneNumbers()) { 8 System.out.print(p.getNumber() + “ “); 9 } 10 System.out.println(“”); 11 }

Problémy způsobuje to, že relace Phone je deklarovaná jako lazy, a proto kolekce Phone nebude instanciována při provádění úvodního dotazu viz. řádek 1. Když je volána metoda getPhoneNumbers() – řádek 7, správce persistence musí dělat dodatečný dotaz, aby dostal telefonní čísla asociovaná se zákazníkem. Nazývá se to problém N + 1, protože se musí udělat N extra dotazů za původním úvodním dotazem. Přitom je snaha omezovat počty dotazů v databázi. Tento problém právě řeší JOIN FETCH. Podívejme se jak bude vypadat modifikovaný dotaz: SELECT c FROM Customer c LEFT JOIN FETCH c.pho nes

Příkaz LEFT JOIN FETCH znova nahraje asociace Phone. Místo N+1 dotazů v databázi, je vykonán pouze jeden úvodní.

Page 76: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

76

EJB QL - Použití DISTINCT Klíčové slovo DISTINCT zabezpečí, že dotaz nevrací duplikáty. SELECT DISTINCT cust FROM Reservation AS res, IN (res.customers) cust

Klauzule WHERE a literály. Klauzule WHERE pracuje velmi podobně jako v SQL SELECT c FROM Customer AS c WHERE c.creditCard.creditCompany.name = ‘Capital On e’ SELECT s FROM Ship AS s WHERE s.tonnage = 1000000.00 SELECT c FROM Customer AS c WHERE c.hasGoodCredit = TRUE

EJB QL - Klauzule WHERE a IN Klauzule IN testuje přítomnost v seznamu literálů. Např. v následujícím příkladě operátor EJB QL IN testuje, zda zákazníci pocházení z uvedených států: SELECT c FROM Customer AS c WHERE c.address.state IN(‘FL’, ‘TX’, ‘MI’, ‘WI ’, ‘MN’)

Hromadné operace UPDATE a DELETE Operaci uvedeme na příkladě, ve kterém chceme přidat všem zákazníkům se jménem Bill Burke $10. UPDATE Reservation res SET res.amountPaid = (res.amountPaid + 10) WHERE EXISTS ( SELECT c FROM res.customers c WHERE c.firstName = ‘Bill’ AND c.lastNam e = ‘Burke’ )

Dále chceme odstranit všechny rezervace vytvořené Bill Burkem: DELETE FROM Reservation res WHERE EXISTS ( SELECT c FROM res.customers c WHERE c.firstName = ‚Bill‘ AND c.lastName= ‚Burke‘ )

Jednoduché entitní nativní dotazy Tyto dotazy vezmou příkaz SQL a implicitně ho mapují do jedné entity založené na mapování metadat, která byla deklarovaná pro tuto entitu. Očekává se, že sloupce vrácené v result setu nativního dotazu, budou perfektně souhlasit s O/R entitním mapováním. Query query = manager.createNativeQuery( “SELECT p.phone_PK, p.phone_number, p.type FROM PHONE AS p”, Phone.class );

Page 77: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

77

Všechny vlastnosti entit musí být vytaženy. Query createNativeQuery(String sql, String mappingN ame)

Tato metoda entitního manažeru dovoluje složitější mapování nativního SQL. Vrací najednou více entit a skalár sloupcových hodnot. Parametr mappingName odkazuje deklarovanou @javax.persistence.SQLResultSetMapping. Uvedená anotace se používá na specifikaci, jak se nativní výsledky SQL napojí (zaháknou zpět) do modelu O/R. Jména sloupců neodpovídají mapování paralelně anotovaných vlastností (properties). Proto je pro ně možné dodat mapování field-to-column použitím anotace @javax.persistence.FieldResult.

Velmi významnou roli v realčních databázích hraje dotazování a jazyk SQL. Tato část proto popisuje a vysvětluje nejdříve Query API (seznam všech dostupných metod), následně pak stránkování výsledků. Poslední část je věnovaná EJB QL – tedy jazyku dotazování, který používá technologie EJB.

Page 78: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

78

8 Session beany

V této kapitole se dozvíte: • co jsou to bezstavové session beany, • jak se zpřístupní vlastnosti prostředí, • jak se využije EJB kontext, • jaký je životní cyklus bezstavových session beanů. Session beany zaplňují mezeru zanechanou entitními beany. Session beany jsou užitečné pro popis interakcí mezi ostatními beany (taskflow) a pro implementaci konkrétních úloh. Mohou být využity ke čtení, aktualizaci a vkládání dat v business procesu. Např. session bean může být použit k udržování (řízení) seznamu všech volných kabin. Použítí session beanů & použití entitní beanů. • Základním pravidlem je, že entitní beany by měly poskytovat bezpečné a konzistentní

rozhraní pro množinu sdílených dat, která definuje koncept. • Session beany obsahují business logiku a entitní beany model perzistentních dat. • Session beany často řídí interakce mezi entitními beany. 8.1 Bezstavové session beany Výhodou těchto session beanů je, že se mohou prohazovat (vyměňovat) mezi EJB objekty, protože nejsou odkázány na údržbu pouze jednoho klienta a neudržují tzv. konverzační stav. Jakmile dokončí obsluhu vyvolání metody, mohou pokračovat v obsluze dalšího EJB objektu (klienta). Protože neudržují konverzační stav, nevyžadují pasivaci a aktivaci. Krátce řečeno bezstavové session beany jsou lehké a rychlé. Bezstavové session beany si nemusejí pamatovat nic z předchozího vyvolání metody. Jedinou výjimkou je informace získaná ze SessionContext a z JNDI ENC nebo odkazů na prostředí, které jsou přímo injektovány do beanu. Bezstavové session beany jsou verzí tradičních EJB aplikací transakčního zpracování, které jsou vykonávány prostřednictvím volání procedur. Procedura se vykonává od začátku do konce a pak teprve vrací výsledek. Po té, co procedura skončí je všechno zapomenuto. Tato omezení neznamenají, že session bean nemůže mít instanční proměnné, které udržují (zaznamenávají) nějaký druh interního stavu. Instanční proměnná může např. zaznamenávat počet vyvolání daného beanu, nebo ukládat data pro ladění. Dalším příkladem mohou být odkazy na živé zdroje např. URL, verifikace kreditní karty prostřednictvím jiného EJB. Vše potřebné v této souvislosti může být získáno prostřednictvím JNDI ENC nebo injektovaných polí. Avšak je důležité si zapamatovat, že tento stav klient nikdy neuvidí. Klient nikdy nemůže předpokládat, že jeho požadavek bude obsluhovat stejný bean. Stejně tak klient neví, kdy se provádí výstup pro ladění. Příklad – proces placení Bean TravelAgent má metodu bookPassage(), která používá bean ProcessPayment. ProcessPayment je bezestavový session bean, který je používán beanem TravelAgent k tomu, aby zákazník zaplatil poplatek za plavbu. Session bean ProcesPayment bude také využíván k jakékoli platbě na lodi např. za nákupy dárků, nebo v butiku a další podobné služby. Proto

Page 79: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

79

logika beanu bude muset vyhovovat všem těmto požadavkům. Platby jsou zaznamenávány ve spaciální tabulce databáze s názvem PAYMENT. Data této tabulky jsou dávkově zpracovávaná pro účely účetnictví a nejsou normálně použita mimo tuto oblast. Pro placení mohou být použity kreditní karty, šeky nebo hotovost. Tabulka PAYMENT CREATE TABLE PAYMENT ( customer_id INTEGER, amount DECIMAL(8,2), type CHAR(10), check_bar_code CHAR(50), check_number INTEGER, credit_number CHAR(20), credit_exp_date DATE )

Business rozhraní : ProcessPayment Business rozhraní (local/remote) pro uvedený bean bude mít následující metody: byCredit(), byCheck(), byCash(). Business rozhraní může být buď vzdálené nebo lokální, ale ne obě současně. Vzdálená business rozhraní vyvolávají klienti na síti (z jiného JVM). Když je klientem vyvolána metoda session beanu vzdáleného rozhraní, hodnoty parametrů a návratová hodnota jsou kopírovány. Tato sémantika volání je známá pod označením call-by-value (volání hodnotou). Lokální rozhraní jsou přístupná pouze v rámci stejného JVM jako session bean. • Tehdy parametry nejsou kopírovány, ale využívá se sémantiky call-by-reference. Session bean ProcessPayment bude potřebovat vytvořit obě rozhraní. Obě rozhraní budou publikována stejným API. • Pro zpřehlednění návrhu budou uvedené rozhraní „podrozhraními“ bázového rozhraní

s názvem com.titan.processpayment.ProcessPayment: import com.titan.domain.*; public interface ProcessPayment { public boolean byCheck(Customer customer, CheckD O check, double amount) throws PaymentException; public boolean byCash(Customer customer, double amount) throws PaymentException; public boolean byCredit(Customer customer, Credi tCardDO card, double amount) throws Pa ymentException; }

Specifikace EJB dovoluje deklarovat vzdálené a lokální rozhraní za předpokladu deklarování stejných metod, což je splněno. Všechny metody vrací booleovskou hodnotu informující, zda byla platba úspěšná. Výjimka je vyhozena např. v případě neplatné karty z hlediska exspirace. Nic z rozhraní ProcessPayment není specifikováno v rezervačním systému. Je to z toho důvodu, že ProcessPayment se může využívat i v jiných typech plateb (mimo rezervace). import javax.ejb.Remote;

Page 80: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

80

@Remote public interface ProcessPaymentRemote extends Proce ssPayment { } package com.titan.processpayment; import javax.ejb.Local; @Local public interface ProcessPaymentLocal extends Proces sPayment { } Entity a parametry Každá z definovaných metod používá jako parametr entitní bean Customer. Entitní beany jsou zpracovávány jako standardní javovské objekty, je proto nutná implementace java.io.Serializable nebo Externalizable. To je důležité, protože bean ProcessPayment zpřístupňuje vnitřní stav entity Customer. Pokud by každé volání metody get entity Customer šlo přes síť, bylo by to velmi nákladné. Doménové objekty: třídy CrediCardDO a CheckDO Business rozhraní Bean ProcessPayment využívá obě třídy, CreditDO a CheckDO: /* CreditCardDO.java */ package com.titan.processpayment; import java.util.Date; public class CreditCardDO implements java.io.Serial izable { final static public String MASTER_CARD = "MASTER _CARD"; final static public String VISA = "VISA"; final static public String AMERICAN_EXPRESS = "A MERICAN_EXPRESS"; final static public String DISCOVER = "DISCOVER" ; final static public String DINERS_CARD = "DINERS _CARD"; public String number; public Date expiration; public String type; public CreditCardDO(String number, Date expirati on, String type) { this.number = number; this.expiration = expiration; this.type = type; } }

/* CheckDO.java */ package com.titan.processpayment; public class CheckDO implements java.io.Serializabl e { public String checkBarCode; public int checkNumber;

Page 81: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

81

public CheckDO (String barCode, int number) { this.checkBarCode = barCode; this.checkNumber = number; } }

Objekty od uvedených tříd jsou doménové objekty. Jsou to serializovatelné javoské třídy ne enterprise beany. Poskytují vhodný mechanismus pro transport dat. Výjimka aplikace (PaymentException) Libovolné vzdálené nebo lokální rozhraní může vyhodit aplikační výjimku. Aplikační výjimka by měla popisovat problém business logiky. Je důležité porozumět jaké výjimky by se měly použít a kdy je použít. Např. výjimky javax.naming.NamingException a javax.sql.SQLException nemají nic co dočinění s business procesy. Výjimka PayableException popisuje konkrétní business problém, který je pravděpodobně opravitelný (řešitelný). Kontejner EJB ošetří každou výjimku, která není podvýjimkou RuntimeException jako výjimku aplikace. Deklarace PaymentException: public class PaymentException extends java.lang.Exc eption { public PaymentException() { super(); } public PaymentException(String msg) { super(msg); } }

Aplikační výjimka je šířena k volajícímu klientovi. Jakákoli instanční proměnná zahrnutá do výjimky by měla být serializovatelná. Neaplikační výjimky jsou vždy sbaleny v EJBException. To znamená, že jakákoli výjimka, kterou vyhodíte a která je typu RuntimeException nebo její potomek, bude zachycena kontejnerem EJB a zabalena do EJBException. Všechny výjimky vyhozené rozhraním Java Persistence jsou výjimky typu RuntimeException. Chování výjimky může být deklarováno explicitně s použitím @javax.ejb.ApplicationException a pomocí XML <application-exception>.

Třída ProcessPaymentBean bezstavový session bean import com.titan.domain.*; import java.sql.*; import javax.ejb.*; import javax.annotation.Resource; import javax.sql.DataSource; import javax.ejb.EJBException; @Stateless public class ProcessPaymentBean implements ProcessP aymentRemote, ProcessP aymentLocal

Page 82: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

82

{ final public static String CASH = "CASH"; final public static String CREDIT = "CREDIT"; final public static String CHECK = "CHECK"; @Resource(mappedName="java:/titanDB") DataSource dataSource; @Resource(name="min") int minCheckNumber = 100; public boolean byCash(Customer customer, double amount) throws PaymentException { return process(customer.getId(), amount, CASH , null, -1, null, null); }

public boolean byCheck(Customer customer, CheckDO c heck, double amount) throws PaymentException { if (check.checkNumber > minCheckNumber) { return process(customer.getId(), amount, C HECK, check.checkBarCode, check.checkNum ber, null, null); } else { throw new PaymentException("Check number i s too low. Must be at least "+minCheckNumber); } } public boolean byCredit(Customer customer, Credi tCardDO card, double amount) throws Pa ymentException { if (card.expiration.before(new java.util.Date ())) { throw new PaymentException("Expiration dat e has passed"); } else { return process(customer.getId(), amount, CRE DIT, null, -1, card.number, new java.sql.Date(card.ex piration.getTime())); } }

private boolean process(int customerID, double amou nt, String type, String checkBarCode, int checkNumber, String creditNumber, java.sql.Date creditExpDate) throws PaymentException { Connection con = null; PreparedStatement ps = null; try { con = dataSource.getConnection(); ps = con.prepareStatement ("INSERT INTO payment (customer_id, amo unt, type,"+ "check_bar_code,check_number,credit_nu mber,"+ "credit_exp_date) VALUES (?,?,?,?,?,?, ?)");

Page 83: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

83

ps.setInt(1,customerID); ps.setDouble(2,amount); ps.setString(3,type); ps.setString(4,checkBarCode); ps.setInt(5,checkNumber); ps.setString(6,creditNumber); ps.setDate(7,creditExpDate); int retVal = ps.executeUpdate(); if (retVal!=1) { throw new EJBException("Payment insert failed"); } return true; } catch(SQLException sql) { throw new EJBException(sql); } finally { try { if (ps != null) ps.close(); if (con!= null) con.close(); } catch(SQLException se) { se.printStackTrace(); } } } }

Třída bean je opatřena anotacemi s využitím anotací @javax.ejb.Stateless: package javax.ejb; @Target(TYPE) @Retention(RUNTIME) public @interface Stateless { String name() default “”; }

Atribut name() identifikuje EJB jméno session beanu. Defóltní jméno pro uvedenou třídu beanu bude ProcessPaymentBean. Třída bean identifikuje svoje vzdálené a lokální rozhraní implementací rozhraní ProcessPaymentRemote a ProcessPaymentLocal. Když je bean rozmisťovaný, kontejner se podívá na rozhraní třídy bean a zkontroluje jestli jsou anotované (opatřené komentářem) @javax.ejb.Local nebo @javax.ejb.Remote. Tato introspekce (nahlédnutí dovnitř) určí vzdálené a lokální rozhraní třídy bean. Alternativně třída bean nemusí implementovat žádné rozhraní a anotace @Local a @Remote mohou být použity přímo ve třídě. @Stateless @Local(ProcessPaymentLocal.class) @Remote(ProcessPaymentRemote.class) public class ProcessPaymentBean { final public static String CASH = "CASH";

Page 84: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

84

final public static String CREDIT = "CREDIT"; final public static String CHECK = "CHECK";

Když se uvedená deklarace použije ve třídě bean, anotace @Local a @Remote vezmou pole rozhraní tříd. Toto není doporučeno dokud nemusíte, protože implementace business rozhraní přímo vyžaduje smlouvu (kontrakt) mezi třídou bean a těmito rozhraními. Třída ProcessPayment Bean Tři deklarované metody (možnosti) placení volají metodu process(), která provádí přidání platby do databáze. Tento způsob redukuje chyby programátora. Metoda process() nevyužívá entitní bean, ale přímo zapisuje data do tabulky PAYMENT s použitím JDBC. 8.2 Zpřístupnění vlastností prostředí (injection) Položky datasource a minCheckNumber jsou příklady session položek, které je inicializované prostředím EJB prostředí. Každý kontejner EJB má svůj vlastní interní registr, kde ukládá konfigurační hodnoty a odkazy na externí zdroje a služby. Tento registr se jmenuje Enterprise Naming Context (ENC). Takové instanční proměnné jsou opatřeny anotací @javax.annotation.Resource. Ta říká kontejneru, že když je vytvářena instance od třídy bean, atributy opatřeny uvedenou anotací musí být inicializovány hodnotami uvedenými v ENC. Když je kontejner EJB rozmístěn, ENC je šířen s metadaty v anotaci jako @Resource a s informacemi uloženými v libovolném popisovači rozmístění EJB XML. Například anotace @Resource která označuje zdrojové datové pole obsahuje atribut mappedName(), který identifikuje externí datový zdroj JDBC, který by měl být mapován do ENC. Anotace @Resource pro položku minCheckNumber identifikuje jmennou hodnotu, která je použita k externí inicializaci uvedeného pole (položky). Právě tyto hodnoty mohou být nakonfigurovány s využitím popisovače rozmístění EJB XML: V uvedené popisovači je jméno min s hodnotou 250, která je využita jako hraniční hodnota. <ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema- instance" xsi:schemaLocation="http://java.sun.com/xml/ ns/javaee http://java.sun.com/xml/ ns/javaee/ejb- jar_3_ 0.xsd" version="3.0"> <enterprise-beans> <session> <ejb-name>ProcessPaymentBean</ejb-name> <env-entry> <env-entry-name>min</env-entry-name> <env-entry-type>java.lang.Integer</env- entry-type> <env-entry-value>250</env-entry-value> </env-entry> </session> </enterprise-beans> </ejb-jar>

XML popisovač rozmístění. Tento popisovač je alternativou k anotacím a rozšiřuje metadata, která nemusíte deklarovat v anotacích. Uvedený popisovač rozmístění, který poskytuje kompletní alternaci pro EJB ProcesPayment

Page 85: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

85

<?xml version="1.0"?> <ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema- instance" xsi:schemaLocation="http://java.sun.com/xml/ ns/javaee http://java.sun.com/xml/ns/jav aee/ejb-jar_3_0.xsd" version="3.0"> <enterprise-beans> <session> <ejb-name>ProcessPaymentBean</ejb-name> <remote>com.titan.processpayment.ProcessPa ymentRemote</remote> <local>com.titan.processpayment.ProcessPay mentLocal</local> <ejb-class>com.titan.processpayment.Proces sPaymentBean</ejb- class> <session-type>Stateless</session-type> <env-entry> <env-entry-name>min</env-entry-name> <env-entry-type>java.lang.Integer</env -entry-type> <env-entry-value>10</env-entry-value> <injection-target> <injection-target-class> com.titan.processpayment.Process PaymentBean </injection-target-class> <injection-target-name>minCheckNumb er</injection-target- name> </injection-target> </env-entry> <resource-ref> <res-ref-name>theDatasource</res-ref-n ame> <res-type>javax.sql.DataSource</res-ty pe> <res-auth>Container</res-auth> <mapped-name>java:/DefaultDS</mapped-n ame> <injection-target> <injection-target-class> com.titan.processpayment.Process PaymentBean </injection-target-class> <injection-target-name>dataSource</ injection-target-name> </injection-target> </resource-ref> </session> </enterprise-beans> </ejb-jar>

XML poskytuje jednu zajímavost, kterou je použití RuntimeException místo EJBException ve třídě beanu. Je to způsobeno tím, že javovský kód nemá žádný odkaz na specifikace EJB. Session kontext Rozhraní javax.ejb.SessionContext poskytuje pohled do prostředí kontejneru EJB. Objekt SessionContext může být použitý jako rozhraní instance beanu ke kontejneru EJB k získání informací o kontextu volání metod a poskytuje rychlý přístup k různým službám EJB. Odkaz na SessionContext získá session bean prostřednictvím anotace @Resource. @Stateless public class ProcessPaymentBean implements Pro cessPayment { @Resource SessionContext ctx; . . . }

Page 86: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

86

SessionContext dovoluje získat informace jako aktuální uživatel, který vyvolal session bean, nebo vyhledat vstupy uvnitř ENC EJB. Rozhraní javax.ejb.SessionContext: public interface javax.ejb.SessionContext extends j avax.ejb.EJBContext { EJBLocalObject getEJBLocalObject() throws Illega lStateException EJBObject getEJBObject() throws IllegalStateExce ption ; MessageContext getMessageContext() throws Illega lStateException; <T> getBusinessObject(Class<T> businessInterface ) throws Illegal StateException; Class getInvokedBusinessInterface(); }

Metody getEJBObject() a getEJBLocalObject() jsou metody EJB 2.1 a vyhazují při vyvolání výjimku. Metoda SessionContext.getBusinessObject() vrací odkaz aktuální bean, který může být vyvolaný jiným klientem. Tato reference je stejná jako reference v Javě. Parametr businessInterface musí být lokální nebo vzdálené rozhraní, takže kontejner ví, zda má vytvořit lokální nebo vzdálený odkaz na bean. Metoda getBusinessObject() dovoluje instanci beanu získat odkaz sama na sebe a ten předat jinému beanu: @Stateless public class A_Bean implements A_BeanRemove { @Resource private SessionContext context; public void someMethod() { B_BeanRemote b = . . . // get a remote refere nce to B_Bean. A_BeanRemote myself = getBusinessObject(A_Bea nRemote.class); b.aMethod(myself); } }

Pro instanci beanu je nepřípustné, aby předala referenci this jinému beanu; místo toho předává odkaz svého vzdáleného nebo lokálního EJB objektu, který instance beanu dostane z jejího SessionContextu. Metoda SessionContext.getInvokedBusinessInterface() dovoluje stanovit, zda váš bean byl vyvolán prostřednictvím vzdáleného, lokálního nebo web service rozhraní. Vrací vyvolané business rozhraní jako třídu. 8.3 EJBContext Třída SessionContext je podtřídou třídy javax.ejb.EJBContext. Třída EJBContext definuje některé důležité metody, které poskytují užitečné informace beanu za jeho běhu. Definice rozhraní EJBContext: package javax.ejb; public interface EJBContext { public Object lookup(String name); // EJB 2.1 only> TimerService public TimerService getTimerService() throws java.lang.IllegalStateException; // security methods public java.security.Principal getCallerPrincipa l(); public boolean isCallerInRole(java.lang.String r oleName);

Page 87: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

87

// transaction methods public javax.transaction.UserTransaction getUser Transaction() throws java.lang.IllegalStateException ; public boolean getRollbackOnly() throws java.lang.IllegalStateException; public vois setRollbackOnly() throws java.lang.IllegalStateException; // deprecated and obsolete methods public java.security.Identity getCallerIdentity( ); public Boolean isCallerInRole(java.security.Iden tity role); public java.util.Properties getEnvironment(); public EJBHome getEJBHome() throws java.lang.IllegalStateException ; public EJBLocalHome getEJBLocalHome() throws java.lang.IllegalStateException; }

Metoda EJBContext.lookup() dovoluje vyhledat vstupy v ENC. Metoda EJBContext.getTimerService() vrací odkaz na Timer Service kontejneru, která dovoluje bezstavovému beanu nastavit oznámení o časových událostech. Timer Service může být také injektována s použitím anotace @Resource. Metoda EJBContext.getCallerPrincipal() se používá k získání objektu java.security.Principal, který reprezentuje klienta, který aktuálně přistupuje k beanu. Objekt Principal může být např. využitý k identifikaci klienta, který dělá aktualizace: @Stateless public class BankBean implements Bank { @Resource SessionCintext context; . . . public void withdraw(int acid, double amoun t) throws Access DeniedException { String modifiedBy = principal.getName(); . . . } }

Metoda EJBContext.isCallerInRole() říká, zda-li klient přistupující k beanu je členem specifické role identifikované jménem role. Viz příklad: Některé uváděné metody jsou k verzi EJB 2.1 a jsou již zastaralé. Např. metody getEJBHome() a getEJBLocalHome() způsobí vyhození výjimky RuntimeException. @Stateless public class BankBean implements Bank { @Resource SessionContext context; public void withdraw(int acid, double amount) th rows AccessDeniedExce ption { if(amount > 10000) { boolean inManager = context.isCallerInRo le(“Manager”); if(!isManager) { // Only Managers can withdraw more than 10 k. throw new AccessDeniedException(); } } } }

Page 88: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

88

8.4 Životní cyklus bezstavových session beanů Životní cyklus bezstavového beanu má pouze dva stavy: Does Not Exit a Metod-Ready Pool. • Stav Metod-Ready Pool je společná oblast instancí objektů bezstavových beanů, které

nejsou v použití. • Stav Does Not Exit reprezentuje stav, kdy z beanu ještě nebyly vytvořeny instance. Když je server EJB poprvé spuštěn, může si vytvořit několik instancí bezstavových beanů (záleží na implementaci), při nedostatku instancí, může server vytvořit další instance a přidat je do společné oblasti. Při přechodu ze stavu Does Not Exit do stavu Metod-Ready Pool jsou provedeny tři operace: 1. je vytvořena instance beanu vyvoláním metody Class.newInstance() 2. kontejner injektuje zdroje, které metadata beanu požadují prostřednictvím anotací nebo

XML popisovače rozmístění. 3. kontejner EJB bude posílat (vysílat) událost post-construction. Třída beanu se může registrovat pro tyto události anotací metody s @javax.annotation.PostConstruct. Tato anotační metoda zpětného volání je volána kontejnerem poté, co je vytvořena instance beanu. Může mít libovolné jméno, musí vracet void, nesmí mít žádné parametry a vyhazovat výjimku no check. Třída bean může definovat pouze jednu metodu @PostConstruct:

@Stateless public class MyBean implements MyLocal { @PostConstruct public void myInit() { }

Bezstavové session beany mohou udržovat otevřená spojení na zdroje po celý jejich životní cyklus. Metoda @PreDestroy by měla zavřít otevřené zdroje před tím, než je instance bezstavového session beanu vyklizena z operační paměti. Život ve stavu Method-Ready Pool Ve stavu method-ready pool je instance připravena obsloužit požadavky klienta. Když klient vyvolá business metodu EJB objektu, volání metody je delegováno na libovolnou volnou instanci ve společné oblasti Method-Ready Pool. Zatímco instance vykonává požadavek, není dostupná jiným EJB objektům. Ihned jak instance skončí, je okamžitě dostupná libovolnému EJB objektu, který ji potřebuje. Instance bezstavových session beanů jsou přiřazeny objektu EJB pouze po dobu trvání volání jedné metody. Když je instance vybrána ze společné oblasti, její ContextSession se mění tak, aby odrážel kontext objektu EJB a klienta vyvolávající metodu. Instance beanu mohou být začleněny do transakčního rozsahu klientské žádosti a mohou přistupovat k informacím SessionContextu, které jsou specifické pro dotaz klienta. Např. bezpečnostní a transakční metody. Poté co instance ukončí obsluhu klienta, je odloučena (oddělena) od objektu EJB a vrací se do společné oblasti připravených metod. Klienti, kteří potřebují vzdálený, nebo lokální odkaz na bezstavový session bean, začnou s injektovaným odkazem nebo vyhledáním bezstavového beanu v JNDI. Vrácená reference nezpůsobí, že instance session beanu bude vytvořena, nebo vytažena ze společné oblasti,

Page 89: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

89

dokud na ni není vyvolána metoda. Metoda PostConstruct je vyvolána pouze jednou a to bezprostředně po vytvoření instance. Přechody mimo společnou oblast: ukončení života instance bezstavového beanu Instance beanu opouští společnou oblast Metod-Ready Pool do stavu Does Not Exit, když je server již dále nepotřebuje, nebo když se server rozhodne zmenšit počet instancí bezstavových session beanů. Stavové session beany Každý stavový session bean (jeho instance) je předurčen pouze pro jednoho klienta po celou dobu života. Zastupuje klienta jako agent klienta. Udržuje tzv. konverzační stav v datových atributech třídy beanu. To umožňuje, aby byly závislé na předchozích voláních jiných metod. Instance beanu udržuje „specifická data klienta“. Stavové session beany umožňují zapouzdřit část business logiky a konverzační stav klienta a přesunout to na server. To dělá aplikaci klienta „tenší“ (thin) a systém jako celek je lépe řiditelný (ovladatelný). Stavový session bean je takto jako klientův agent, který vykoná (zařídí) řadu úloh (taskflow) a interakcí s dalšími beany. Takto zapouzdřuje řízení toku úloh a představuje jednoduché rozhraní, které ukrývá detaily vzájemně závislých operací nad DB a jinými beany. Nastavení TravelAgent – entita Reservation TravelAgent používá následující entity: Cabin, Cruise, Reservetion, Customer. Koordinuje interakce těchto entitních beanů, aby zabukoval pasažera na plavbu (cruise). Bude provedena modifikace entity Reservation, aby se její další vazby na entity provedly najednou. Bude definován další konstruktor, který to bude provádět. Volání konstruktoru umožní se vyhnout mnoha dalším volání set metod.

@Entity public class Reservation implements java.io.Seriali zable { private int id; private Date date; private double amountPaid; private Cruise cruise; private Set<Cabin> cabins = new HashSet<Cabin>() ; private Set<Customer> customers = new HashSet<Cu stomer>(); public Reservation() {} public Reservation(Customer customer, Cruise cru ise, Cabin cabin, double price, Da te dateBooked) { setAmountPaid(price); setDate(dateBooked); setCruise(cruise);

Page 90: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

90

Set cabins = new HashSet(); cabins.add(cabin); this.setCabins(cabins); Set customers = new HashSet(); customers.add(customer); this.setCustomers(customers); }

TravelAgent Cílem tohoto stavového beanu je vytvořit rezervaci pro danou plavbu. Bude vytvořeno pouze vzdálené rozhraní – lokální rozhraní je podobné a nebude se aktuálně využívat. Vzdálené rozhrani TravelAgent session bean. Bean potřebuje znát cruise, cabin, customer k vytvoření rezervace. Identifikace klienta je podle jména a přijmení. Po vyhledání (určení) customer, cruise, cabin, provede metoda bookPassage() rezervaci. import com.titan.processpayment.CreditCardDO; import javax.ejb.Remote; import com.titan.domain.Customer; import com.titan.domain.Address; @Remote public interface TravelAgentRemote { public Customer findOrCreateCustomer(String fir st, String last); public void updateAddress(Address addr); public void setCruiseID(int cruise); public void setCabinID(int cabin); public TicketDO bookPassage(CreditCardDO card, double price) throws IncompleteConversationalState; } // Výjimka znamená, že metoda nemá dostatek pot řebných informací public class IncompleteConversationalState extends java.lang.Exception { public IncompleteConversationalState () { super (); } public IncompleteConversationalState (String msg ) { super (msg); } }

TicketDO – doménový objekt Třída TicketDO je definovaná jako objekt předávaný hodnotou (pass-by-value object). Tato třída umožňuje oproti jiným možnostem, zaslat klientovi společně všechny informace, které je třeba.

import com.titan.domain.Cruise; import com.titan.domain.Cabin; import com.titan.domain.Customer;

Page 91: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

91

public class TicketDO implements java.io.Serializab le { public int customerID; public int cruiseID; public int cabinID; public double price; public String description; public TicketDO(Customer customer, Cruise cruis e, Cabin cabin, double price) { description = customer.getFirstName( )+ " " + customer.getLastName( ) + " has been booked for the " + cruise.getName( ) + " cruise on s hip " + cruise.getShip( ).getName( ) + ". \n" + " Your accommodations include " + cabin.getName( ) + " a " + cabin. getBedCount( ) + " bed cabin on deck level " + cabin .getDeckLevel( ) + ".\n Total charge = " + price; customerID = customer.getId(); cruiseID = cruise.getId(); cabinID = cabin.getId(); this.price = p rice; } public String toString( ) { return description; } }

Klientův pohled TravelAgent je využívaný aplikací v Javě GUI poli, do kterých se zapíší požadavky zákazníka (customer) na plavbu a kabinu. Context jndi = getInitialContext(); Object ref = jndi.lookup(“TravelAgentBean/remote ”) TravelAgentRemote agent = (TravelAgentRemote) PortableRemotaObject.narrow(ref, TravelAgentRemo te.class);

Kód vyhledá TravelAgent bean v JNDI. Při každém vyhledání stavového session beanu se vytvoří nová instance agent. Customer cust = agent.findOrCreateCustomer(textFiel d_firstName.getText(), testField_lastName.getText());

Změny adresy provede rovněž agent Address updatedAddress = new Address(textField_street.getText(), … ); agent.updateAddress(updatedAddress);

Dále je třeba určit cruise a cabin Integer cruise_id = new Integer(textField_cruiseNum ber.getText()); Integer cabin_id = new Integer(textField_cabinNumber.get Text()); agent.setCruiseID(cruise_id); agent.setCabinID(cabin_id);

Page 92: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

92

Na konci procesu rezervace agent doplní credit card a price pro dokončení transakce: String cardNumber = textField_cardNumber.getText(); Date date = dateFormatter.parse(textField_cardExpir ation.gettext()); String cardBrand = textField_cardBrand.getText()); CreditCardDO card = new CreditCardDO9cardNumber, da te, cardBrand); double price = double.valueOf(textField_cruisePrice.getText()).dou bleValue(); TicketDO ticket = agent.bookPassage(card, price); Printingservice.print(Ticket);

TravelAgent bean upravený bean import com.titan.processpayment.*; import com.titan.domain.*; import javax.ejb.*; import javax.persistence.*; import javax.annotation.EJB; import java.util.Date; @Stateful public class TravelAgentBean implements TravelAgent Remote { @PersistenceContext(unitName="titan") private EntityManager entityManager; @EJB private ProcessPaymentLocal processPayment ;

private Customer customer; private Cruise cruise; private Cabin cabin; public Customer findOrCreateCustomer(String fir st, String last) { try { Query q = entityManager.createQuery("fro m Customer c where c.firstName = :first an d c.lastName = :last"); q.setParameter("first", first); q.setParameter("last", last); this.customer = (Customer)q.getSingleRes ult(); } catch (NoResultException notFound) { this.customer = new Customer(); this.customer.setFirstName(first); this.customer.setLastName(last); entityManager.persist(this.customer); } return this.customer; } public void updateAddress(Address addr) { this.customer.setAddress(addr); this.customer = entityManager.merge(custome r); } public void setCabinID(int cabinID) { this.cabin = entityManager.find(Cabin.class , cabinID); if (cabin == null) throw new NoResultExcept ion("Cabin not found");

Page 93: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

93

} public void setCruiseID(int cruiseID) { this.cruise = entityManager.find(Cruise.cla ss, cruiseID); if (cruise == null) throw new NoResultExcep tion("Cruise not found"); }

Metoda představuje koncept taskflow. @Remove public TicketDO bookPassage(CreditCardDO card, double price) throws IncompleteConversationalState { if (customer == null || cruise == null || c abin == null) { throw new IncompleteConversationalState ( ); } try { Reservation reservation = new Reservati on( customer, cruise, cabin, pri ce, new Date( )); entityManager.persist(reservation); processPayment.byCredit(customer, card, price); TicketDO ticket = new TicketDO(customer , cruise, cabin, price); return ticket; } catch(Exception e) { throw new EJBException(e); } } }

Anotace @Remote u metody bookPassage() říká EJB kontejneru, že když je metoda dokončena, již dále nepotřebuje session bean. Kontejner proto odstraní session bean z kontejneru. Životní cyklus stavových session beanů Stavové session beany nepoužívají instance pooling (sdílení instancí). Pokud instance stavových session beanů zahálejí, jsou „vypovězeny“ z paměti. Z toho důvodu, musí být každá instance stavového session beanu nejdříve pasivována, před tím, než je „vypovězena“ z paměti. Pasivace zabezpečí uchování konverzačního stavu instance. Následně pak musí být aktivovaná – konverzační stav je získán zpět.

Page 94: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

94

Obr. 8.2 Životní cyklus stavových beanů Stavy session beanu Does Not Exist State – session neexistuje v paměti, nebyla dosud vytvořena instance Method-Ready State – v tomto stavu instance beanu může sloužit požadavkům od klientů

• Transakce do Method-Ready State • Když klient vyvolá první metodu stavového session beanu, začíná jeho život.

Kontejner vyvolá metodu newInstance() třídy beanu. • Kontejner injektuje všechny závislosti do instance beanu. • Kontejner vyvolá @PostConstruct callback metody, pak pokračuje s aktuální první

metodou.

– Život ve stavu Method-Ready. – Během tohoto stavu instance neobsluhuje požadavky klienta a udržuje

konverzační stav a otevřené zdroje ve svých instančních proměnných. – Transakce mimo stav Method-Ready

– Instance beanu může opustit tento stav buď do stavu Passivete nebo stavu Does Not Exist.

– Je-li instance beanu odstraněna, dostane se do stavu Does Not Exist. – Odstranění se dosáhne vyvoláním metody, která je anotovaná jako @Remove. – Kontejner může také instanci beanu do stavu Does Not Exist po uplynutí

timeoutu. To však není možné, pokud probíhá transakce. Passivated State

– Je to doba, kdy instance beanu neslouží metodám klienta. – V tomto stavu kontejner uloží konverzační stav a vykáže instanci beanu z paměti. – Konverzační stav instance beanu může sestávat z hodnot primitivních typů,

serializovatelných objektů a následujících speciálních typů:

– javax.ejb.SessionContext – javax.jta.UserTransaction

Does Not Exist

Method-Ready Passive

Class.newInstance() injections @PostConstruct

@Predestroy timeout timeout

@PrePassivate

@PostActivate

create() business method

instance throws system exception

Page 95: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

95

– javax.naming.Context – javax.persistence.EntityManager – javax.persistence.EntityManagerFactory – odkazy na další instance beanů

Konverzační stav je obyčejně serializován pro další uchování. Stavové session beany a rozšířený perzistentní kontext Dosud byt využíván perzistentní kontext ohraničený transakcí. Na konci transakcí se proto prováděla operace: EntityManager.merge(). Rozšířený perzistentní kontext umožňuje, aby všechny nahrané (loaded) entity zůstaly stále řízené po libovolném volání metody TravelAgentBean. import static javax.persistence.PersistenceContextT ype.EXTENDED; @Stateful public class TravelAgentBean implements TravelAgent Remote { @PersistenceContext(unitName="titan”, type=EXTE NDED) private EntityManager entityManager; public void updateAddress(Address addr) { customer.setAddress(addr); } . . . } //Není pot řeba p řidávat metodu merge() entitního managera

Vnořené stavové session beany Při injektování stavový session bean do EJB s anotací @EJB je vytvořena session pro takto injektovanou instanci: @Stateful public class ShoppingCartBean implements Shopi ngCart { @EJB AnotherStatefulLocal another; @Remove void checkout{ } }

Když je reference stavového session beanu injektovaná do jiného stavového session beanu, obsažený (vnější) bean vlastní injektovanou session. Při odstranění vnějšího beanu je také odstraněn vnořený bean. Tato konstrukce je výhodná pro agregaci konverzace business procesů bez starostí o řízení jejich životního cyklu.

Bezstavové session beany hrají velmi významnou roli. Kapitola vysvětluje jejich použití, použití EJB kontextu a jejich životní cyklus.

Page 96: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

96

9 Beany řízené zprávami

V této kapitole se dozvíte: • na co se využívají beany řízené zprávami.

Všichni dodavatelé EJB 3.0 musí podporovat poskytovatele JMS (JavaMessageService). Bez ohledu zda systém má vlastní vestavěné JMS, nebo podporuje jiné JMS, poskytovatel JMS (provider) je absolutní nezbytnost pro podporu beanů řízených zprávami. Tím je garantovaný jak příjem, tak i vysílání beanů řízených zprávami. JMS je na dodavatelích nezávislé API, které může být využité v systému posílání zpráv (message-oriented middleware). Systémy posílání zpráv (Enterprise Messaging systems) umožňují výměnu zpráv mezi programovými aplikacemi v počítačové síti. Role JMS se neliší od role JDBC; podobně jako API JDBC poskytuje společné rozhraní pro přístup k různým relačním databázím, JMS poskytuje na dodavatelích nezávislý přístup k systému posílání zpráv. Programové aplikace, které používají API JMS pro odesílání a přijímání zpráv jsou portovatelné z jednoho dodavatele JMS na jiného. Aplikace, které používají JMS se nazývají JMS klienti a systém posílání zpráv, který řídí směrování a doručování zpráv se nazývá poskytovatel JMS (JMS provider). JMS aplikace je složena z mnoha JMS klientů a obyčejně jednoho poskytovatele JMS. JMS klient, který posílá zprávu se nazývá producent (producer) a JMS přijímající klient se nazývá konzument (consumer). JMS klient může být jak producentem, tak spotřebitelem. EJB enterprise beany všech typů, mohou využívat JMS k posílání zpráv. Tyto zprávy jsou „konzumovány“ jinými javovskými aplikacemi, nebo beany řízenými zprávami. K posílání zpráv z enterprise beanů se používá messaging service, známá také jako message broker nebo router. Reimplementace TravelAgent s využitím JMS Kód ukazuje, jak modifikovat metodu bookpassage() tak, aby TravelAgent posílal jednoduché textové zprávy na základě popisu obsaženého v objektu TicketDO.

@Resource(mappedName=“ConnectionFactoryNameGoesHere ”) private ConnectionFactory connectionFactory; @Resource(mappedName=“TicketTopic”); private Topic topic; @Remove public TicketDO bookPassage(CreditCardDO card, doub le price) throws IncompleteConversationalState { if (customer == null || cruise == null || cabin == null) { throw new IncompleteConversationalState ( ); } try { Reservation reservation = new Reservation(

Page 97: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

97

customer, cruise, cabin, p rice, new Date( )); entityManager.persist(reservation); processPayment.byCredit(customer, card, pr ice); TicketDO ticket = new TicketDO(customer, c ruise, cabin, price); Connection connect = factory.createConnection( ); Session session = connect.createSession(true, 0); MessageProducer producer = session.createPoduc er(topic); TextMessage textMsg = session.createTextMessag e(); textMsg.setText(tisketDescription); producer.send(textMsg); connect.close(); return ticket; } catch(Exception e) { throw new EJBException(e); } }

K zaslání zprávy je třeba mít spojení (connection) na JMS provider a cílovou adresu zprávy. JMS connection factory umožní spojení k poskytovateli. Cílová adresa je identifikovaná s objektem Topic. Oba objekty – connection a topic se získají pomocí @javax.annotation.Resource k přímému injektování do položek TravelAgent beanu: @Resource(mappedName=“ConnectionFactoryNameGoesHere ”) private ConnectionFactory connectionFactory; @Resource(mappedName=“TicketTopic”); private Topic topic;

ConnectionFactory je podobná DataSource JDBC, ale DataSource poskytuje napojení do databáze, ConnectionFactory poskytuje napojení na message router (směrovač zpráv). Objekt Topic reprezentuje síťově nezávislou destinaci, které je adresovaná zpráva. Zprávy JMS nejsou přímo posílány aplikaci, jsou posílány do topics nebo front. topic je analogické k emailovému seznamu nebo newsgroup; aplikace dostává a posílá zprávy z/na topic. Když JMS klient dostane zprávu od topic, je mu řečeno, aby se subskriboval (přihlásil) k tomu topic. JMS oddělí aplikace tím, že jim dovolí zasílat si navzájem zprávy prostřednictvím místa určení (destinace), které slouží jako virtuální kanál. Fronta je dalším typem destinace. Connection and Session ConnectionFactory se používá na vytvoření Connection, což je aktuální propojení na poskytovatele JMS. Connection connect = connectionFactory.createCo nnection(); Session session = connection.createSession(true , 0);

Session dovoluje seskupovat akce posílání a přijímání zpráv, v tom případě potřebujete pouze jednu session. Vícenásobné session jsou výhodné při posílání a přijímání zpráv více různým vláknům, pro každé vlákno je třeba vytvořit vlastní session.

Page 98: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

98

Metoda createSession() má dva parametry: • boolean transacted • int acknowledgeMode Tyto argumenty jsou ignorovány za běhu, protože kontejner EJB řídí oba mody. Doporučené nastavení je true a 0. Po použití je dobré connection uzavřít: connection.close(); K vytvoření MessageProducer se používá Session, který pošle zprávu z beanu TravelAgent na destinaci specifikovanou objektem Topic. Jakýkoli klient, který je subskribován k tomuto topic, obdrží kopii zprávy: MessageProducer producer = session.createProducer(t opic); TextMessage textMsg = session.createTextMessage(); textMsg.setText(ticketDestination); producer.send(textMsg);

V JMS je zpráva javovský objekt s:

• header je složen z doručujících informací a metadat

• message body nese data aplikace, které mohou mít několik forem:

– text – serializovaný objekt – stream bytů apod.

API JMS definuje několik typů zpráv: textMessage, MapMessage, ObjectMessage apod. a poskytuje metody pro doručení a přijímání těchto zpráv. Např. u TravelAgent chceme změnit posílání TextMessage na MapMessage TicketDO ticket = new TicketDO(customer, cruise, ca bin, price); . . . MessageProducer producer = session.createProducer(t opic); MapMessage mapMsg = session.createMapMessage(); mapMsg.setInt(“CustomerID”, ticket.customerID.intVa lue()); mapMsg.setInt(“CruiseID”, ticket.cruiseID.intValue( )); mapMsg.setInt(“CabinID”, ticket.cabinID.intValue()) ; mapMsg.setDouble(“Price”, ticket.price); Atributy MapMessage(CustomerID, CruiseID, CabinID a Price) mohou být zpřístupn ěny pomocí jména od t ěch JMS klient ů, kte ří je dostanou. Jako alternativa TravelAgent bean by mohl být modif ikovaný na použití typu ObjectMessage, která by dovolila poslet celý objekt TicketDO jako zprávu s využitím serializace. TicketDO ticket = new TicketDO(customer, cruise, ca bin, price); . . . MessageProducer producer = session.createProducer(t opic); ObjectMessage objectMsg = session.createObjectMessa ge(); objectMsg.setObject(mapMsg);

Page 99: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

99

JMS klient aplikace Pro objasnění si uvedeme javovskou aplikaci, která pouze přijímá a zpracovává zprávy týkající se rezervací. Aplikace je jednoduchý klient JMS, který tiskne popisy každého lístku (ticket) a přijímá zprávy. Bean TravelAgent používá TextMessage k zaslání popisu každého lístku klientům JMS.

import javax.jms.Message; import javax.jms.TextMessage; import javax.jms.ConnectionFactory; import javax.jms.Connection; import javax.jms.Session; import javax.jms.Topic; import javax.jms.JMSException; import javax.naming.InitialContext; public class JmsClient_1 implements javax.jms.MessageListener { public static void main(String [] args) throws E xception { if(args.length != 2) throw new Exception(“Wrong number of argumen ts”); new JmsClient_1(args[0], args[1]); while(true) {Thread.sleep(10000); } } public JmsClient_1(String factoryName, String to picName) throw s Exception { InitialContext jndiContext = getInitialContex t (); ConnectionFactory factory = (ConnectionFactory) jndiContext.lookup ("ConnectionFactor yNameGoesHere"); Topic topic = (Topic) jndiContext.lookup (“Topi cNameGoesHere"); Connection connect = factory.createConnection() ; Session session = connect.createSession(false,Session.AUTO_AC KNOWLEDGE); MessageConsumer consumer = session.createConsum er(topic); consumer.setMessageListener(this); System.out.println ("Listening for messages on titan-TicketQueue..."); connect.start (); }

public void onMessage (Message message) { try { TextMessage textMsg = (TextMessage)message ; String text = textMsg.getText( ); System.out.println (“\n RESERVATION RECEIV ED:\n” + text"); } catch (JMSException jmsE) { jmsE.printStackTrace (); } }

Page 100: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

100

public static InitialContext getInitialContext ( ) // create vendor-specific JNDI context here } }

Metoda getInitialContext() pro server JBoss public static InitialContext getInitialContext() { Properties env = new Properties(); env.put(Context.SECURITY_PRINCIPAL, “guest”); env.put(Context.SECURITY_CREDENTIALS, “guest”); env.put(Context.INITIAL_CONTEXT_FACTORY, “ org.jboss.security.jndi.JndiLoginInitial ContextFactory”); env.put(Context.PROVIDER_URL, “ jnp://hostname:1 099”); return new InitialContext(env); }

Hlavní rozdíl je v tom, že objekt Session se používá k vytvoření MessageConsumer než k vytvoření MessageProducer. MessageConsumer je navržen na zpracování přicházejících zpráv, které jsou publikovány do svých Topic: Session session = connect.createSession(false, Session.AUTO_ACKNOW LEDGE); MessageConsumer consumer = session.createConsumer(t opic); consumer.setMessageListener(this); connect(start);

MessageConsumer může přijímat zprávy přímo, nebo delegovat zpracování zpráv na javax.jms.MessageListener. Objekt MessageListener implementuje metodu onMessage(), která je vyvolána pokaždé, když přijde nová zpráva do topic. Základní výhodou posílání zpráv s využitím JMS je asynchronnost. Klient po zaslání zprávy nečeká na odpověď. Protože zasílání zpráv ve své podstatě odděluje producenta od spotřebitele, k příjemci nejsou šířeny transakce a bezpečnostní kontext odesílatele. Příjemce může dostat zprávu se značným zpožděním vlivem počítačové sítě. JMS modely posílání zpráv JMS poskytuje dva typy modelů posílání zpráv:

• publish-and-subscribe určen pro vysílání one-to-many

• point-to-point určen pro vysílání one-to-one.

Publish-and-subscribe • Jeden producent může poslat zprávu několika konzumentům. • Konzument se může subskribovat do topic. • Jakákoli zpráva adresovaná topic je doručena všem konzumentům topic. • Je to model typu push. Zprávy jsou automaticky bezprostředně zasílány konzumentům,

bez jejich další žádosti. • JMS klienti se mohou odhlašovat a zase připojovat do systému posílání zpráv.

Page 101: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

101

Point-to-point • Tento model dovoluje jak synchronní, tak asynchronní posílání zpráv prostřednictvím

virtuálních kanálů známých jako queues (fronty). • V p2p modelu posílání zpráv, se zprávy dostávají z fronty na požádání. • Fronta může mít více producentů, ale pouze jednoho konzumenta. Jaký model zvolit? Oba modely jsou rovnocenné. Kritérium: který model lépe splní požadavky aplikace. Pub/sub model má hodně příjemců kopií zpráv. Publisher se nezajímá, jestli nějaký subscriber je odpojený, nebo ztratil zprávu z jiných důvodů. U modelu one-to-one skutečně záleží na doručení a odeslání každé zprávy. U modelu pub/sub je možné dělat filtrování rozesílání zpráv prostřednictvím speciálních topic. Model one-to-one je vhodný když chceme, aby konkrétní příjemce dostal všechny zprávy. Session beany by neměly přijímat zprávy JmsClient_1 byl navržen, aby konzumoval zprávy zaslané beanem TravelAgent. Ovšem i další session beany mohly obdržet také tyto zprávy – není dobré řešení. Session beany odpovídají na volání EJB klientů, ale nemohou být naprogramováni na to, aby odpovídaly na zprávy JMS jak to dělají beany řízené zprávami. Je možné ale vyvinout session bean, který umí konzumovat JMS zprávu z business metody, ale EJB klient musí tuto metodu nejdříve zavolat. Např. když je vyvolána business metoda na hypotetickém EJB, tak nastaví JMS sesssion a pak se pokusí přečíst zprávu z fronty: @Stateless public class HypotheticalBean implements Hypothetic alRemote { @Resource(mappedName=“ConnectionFactory”) private ConnectionFactory factory; @Resource(mappedName=“MyQueue”); private Queue queue; public String businessMethod() { try { Connection connect = factory.createConnection( ); Session session = connect.createSession(true, 0); MessageConsumer receiver = session.createConsu mer(queue); TextMessage textMsg = (TextMessage)receiver.re ceive(); connect.close(); return textMsg.getText(); } catch(Exception e) { throw new EJBException(e); } . . . }

Konzument zprávy proaktivně přinese zprávu z fronty. I když je tato operace naprogramovaná správně, je nebezpečná, protože volání metody MessageConsumer.receive() blokuje vlákno, dokud není zpráva dostupná. Pokud zpráva nikdy nedorazí, je vlákno trvale blokované. Nikdo nepošle zprávu.

Page 102: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

102

JMS – beany řízené zprávami Beany řízené zprávami jsou bezstavové, transakčně uvědomělé komponenty na straně serveru pro zpracování asynchronních zpráv doručených prostřednictvím JMS. Kontejner řídí konkurentnost a poskytuje další infrastrukturu pro beany řízené zprávami. Bean řízený zprávami je kompletní enterprise bean, podobně jako session nebo entitní beany, ale jsou zde některé důležité odlišnosti. Bean řízený zprávami má třídu, ale nemá vzdálené a lokální rozhraní – odpovídá pouze na asynchronní zprávy. Bean řízený zprávami - ReservationProcess Tento bean získává JMS zprávy, které ho uvědomují o nové rezervaci. ReservationProcess je automatizovaná verze beanu TravelAgent, která zpracovává rezervace prostřednictvím JMS. Tyto zprávy mohou přicházet od jiné aplikace, nebo od aplikace jiné organizace, eventuálně od jiného beanu TravenAgent. Když obdrží bean ReservationProcess zprávu, vytvoří nový bean Reservation, přidá ho do databáze, zpracuje platbu s využitím beanu ProcessPayment a odešle lístek (ticket). Následuje částečná definice třídy. Metoda onMessage() obsahuje business logiku; je podobná business logice v metodě bookPassage() beanu TravelAgent.

import com.titan.domain.*; import com.titan.processpayment.*; import com.titan.travelagent.*; import java.util.Date; import javax.annotation.*; import javax.ejb.*; import javax.jms.*; import javax.persistence.*; @MessageDriven(activationConfig={ @ActivationConfigProperty(propertyName="destinat ionType", propertyValue="javax.jms.Que ue"), @ActivationConfigProperty(propertyName=“messageS elector", propertyValue=“MessageFormat = ‘Versi on 3.4’”), @ActivationConfigProperty(propertyName=“acknowle dgeMode”, propertyValue=“Auto-acknowledge”)}) public class ReservationProcessorBean implements ja vax.jms.MessageListener { @PersistenceContext(unitName= "titan") private EntityManager em; @EJB private ProcessPaymentLocal process;

@Resource(mappedName=„ConnectonFactory") private ConnectionFactory connectionFactory; public void onMessage(Message message) { System.out.println("Received Message"); try { MapMessage reservationMsg = (MapMessage)mess age; int customerPk = reservationMsg.getInt("Cust omerID"); int cruisePk = reservationMsg.getInt("Cruise ID"); int cabinPk = reservationMsg.getInt("CabinID ");

Page 103: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

103

double price = reservationMsg.getDouble("Pri ce"); // get the credit card Date expirationDate = new Date(reservationMsg.getLong("CreditCa rdExpDate")); String cardNumber = reservationMsg.getString ("CreditCardNum"); String cardType = reservationMsg.getString(" CreditCardType"); CreditCardDO card = new CreditCardDO(cardNum ber, expir ationDate, cardType);

Customer customer = em.find(Customer.class, customerPk); Cruise cruise = em.find(Cruise.class, cruise Pk); Cabin cabin = em.find(Cabin.class, cabinPk); Reservation reservation = new Reservation(cu stomer, cruise, cabin, price, new Date()) ; em.persist(reservation); process.byCredit(customer, card, price); TicketDO ticket = new TicketDO(customer,crui se,cabin,price); deliverTicket(reservationMsg, ticket); } catch(Exception e) { throw new EJBException(e); } }

public void deliverTicket (MapMessage reservatio nMsg, TicketDO ticket) throws JMSException, Na mingException { Queue queue = (Queue)reservationMsg.getJMSRep lyTo(); // Queue queue = (Queue) //new InitialContext().lookup ("queue/titan-T icketQueue"); Connection connect = connectionFactory.create Connection(); Session session = connect.createSession(true, 0); MessageProducer sender = session.createProduc er(queue); ObjectMessage message = session.createObjectM essage(); message.setObject(ticket); sender.send(message); connect.close(); } }

Událostmi řízený kontext Objekt kontextu beanu řízeného zprávami je podobný co do funkcionality kontextu javax.ejb.SessionContext. Tento objekt může být injektovaný prostřednictvím anotace @javax.annotation.Resource: @Resource MessageDrivenContext context;

MessageDrivenContext pouze rozšiřuje EJBContext; nepřidává žádnou metodu. Definice EJBContext. MessageDrivenContext využívá pouze transakční metody. public interface EJBContext {

Page 104: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

104

// transaction methods public javax.transaction.UserTransaction getUserT ransaction() throws java.lang.illegalStateException; public boolean getRollbackOnly() throws java.lang .IllegalStateException; public void setRollbackOnly() throws java.lang.Il legalStateException; // EJB home methods public EJBHome getEJBHome(); public EJBLocalHome getEJBLocalHome(); // security methods public java.security.Principal getCallerPrincipal (); public boolean isCallerInRole(java.lang.String ro leName); // deprecated methods public java.security.Identity getCallerIdentity() ; public boolean isCallerInRole(java.security.Ident ity role); public java.util.Properties getEnvironment(); }

MDBs (message driven beans) obyčejně provádějí transakce inicializované kontejnerem nebo beanem. Takže transakční metody umožňují MDB řídit svůj kontext. Transakční kontext není šířený od JMS odesílatele; je inicializovaný buď kontejnerem, nebo je explicitně inicializovaný pomocí javax.jta.UserTransaction. MDBs mají také přístup k jejich vlastní JNDI ENCs, což poskytuje MDB instancím přístup k entitám prostředí, jiným beanům a zdrojům. Např. MDB ReservationProcessor využívá ENC k získání odkazů na EntityManager Titanu, bean ProcessPayment a JMS ConnectionFactory a Queue pro odeslání lístků. MDBs obyčejně implementují rozhraní javax.jms.MessageListener, které deklaruje metodu onMessage(). package javax.jms; public interface MessageListener { public void onMessage(Message message); }

MDBs mohou ale také implementovat jiná rozhraní. Při příchodu zprávy, přejde řízení na callback metodu onMessage(). Když metoda končí, MDB je připraven zpracovat novou zprávu. V beanu ReservationProcessor metoda onMessage() extrahuje (rozdělává) informace týkající se rezervace ze zprávy MapMessage a využívá tyto informace k vytvoření rezervace v systému. public void onMessage(Message message) { try { MapMessage reservationMsg = (MapMessage)me ssage; int customerPk = reservationMsg.getInt("Cu stomerID"); int cruisePk = reservationMsg.getInt("Crui seID"); int cabinPk = reservationMsg.getInt("Cabin ID"); double price = reservationMsg.getDouble("P rice"); // get the credit card

Page 105: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

105

Date expirationDate = new Date(reservationMsg.getLong("Credit CardExpDate")); String cardNumber = reservationMsg.getStri ng("CreditCardNum"); String cardType = reservationMsg.getString ("CreditCardType"); CreditCardDO card = new CreditCardDO(cardN umber, expir ationDate, cardType);

JMS je často používán jako integrující bod na aplikace business-to-business (B2B), takže je snadné si představit reprezentaci zprávy přicházející od Titanových business partnerů. Bean ReservationProcessor potřebuje přístup k entitám Customer, Cruise a Cabin, aby mohl zpracovat rezervaci. Zpráva MapMessage obsahuje primární klíče těchto entit. Customer customer = em.find(Customer.class, cu stomerPk); Cruise cruise = em.find(Cruise.class, cruisePk ); Cabin cabin = em.find(Cabin.class, cabinPk);

Získání příslušných entit v metodě onMessage() pomocí injektovaného em (EntityManager) Vytvoření rezervace: Reservation reservation = new Reservation(custo mer, cruise, cabin, price, new Date()); em.persist(reservation); process.byCredit(customer, card, price); TicketDO ticket = new TicketDO(customer,cruise ,cabin,price); deliverTicket(reservationMsg, ticket);

MDB podobně jako jiný session bean může přistupovat k jiným session beanům a použít tento bean k dokončení úlohy. MDB může řídit proces, být v interakci s jinými beany, stejně jako se zdroji. Zaslání zprávy z beanu řízeném zprávami MDB mohou také s využitím JMS posílat zprávy. Metoda deliveryTicket() posílá informace o lístku na místo definované posílajícím klientem JMS. public void deliverTicket (MapMessage reservationMs g, TicketDO ticket) throws JMSException, NamingException { Queue queue = (Queue)reservationMsg.getJMSRep lyTo(); // Queue queue = (Queue) //new InitialContext().lookup ("queue/titan-T icketQueue"); Connection connect = connectionFactory.create Connection(); Session session = connect.createSession(true, 0); MessageProducer sender = session.createProduc er(queue); ObjectMessage message = session.createObjectM essage(); message.setObject(ticket); sender.send(message); connect.close(); }

Header zprávy obsahuje routovací informace a může také obsahovat properties pro filtrování zprávy a další atributy např. JMSReplyTo. Odesílatel zprávy může nastavit JMSReplyTo na libovolnou, prostřednictvím poskytovatele JMS, dosažitelnou destinaci. V případě zprávy o rezervaci, může atribut JMSReplyTo být nastaven na frontu (queue), na kterou by měl být lístek poslán. Jiná aplikace může zpřístupnit tuto frontu a přečíst lístky a distribuovat je zákazníkům.

Page 106: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

106

Atribut JMSReplyTo může být také využit na hlášení o aplikačních chybách. Např. cabin již může být rezervovaná a aplikační chybová zpráva může prostřednictvím zmíněného atributu vysvětlit, proč neproběhla rezervace úspěšně. MDBs jsou identifikovatelné prostřednictvím anotace @jaxax.ejb.MessageDriven, nebo alternativně prostřednictvím popisovače rozmístění. Většinou jsou MDBs rozmisťovány spolu s dalšími beany, na které se odkazují. Např. bean reservationProcess používá bean ProcessPayment a EntityManager Titan – proto jsou rozmístěny společně. MDBs mohou obdržet zprávy od libovolných poskytovatelů zpráv (message providers). Konfigurace musí být proto velmi pružná, aby byla schopna popsat proprietální vlastnosti, které mají různí poskytovatelé. K usnadnění této situace, atribut @MessageDriven.activationConfig() obsahuje pole anotací @ActivationConfigProperty. Anotace jsou pouze dvojice jméno a hodnota popisující konfiguraci. @MessageDriven(activationConfig={ @ActivationConfigProperty( propertyName=“destinationType”, propertyValue=“javax.jms.queue”), @ActivationConfigProperty( propertyName=“messageSeector”, propertyValue=“MessageFormat = ‘Version 3.4’”), @ActivationConfigProperty( propertyName=“acknowledgeMode”, propertyValue=“Auto-acknowledge”)}) public class ReservationProcessBean implements java x.jms.MessageListener { . . . }

Message selector Message selector umožňuje, aby MDBs byly více selektivní v přijímání zpráv od konkrétní topic nebo queue. Message selector používá vlastností Message jako kritéria v podmínkovém výrazu. Podmínkový výraz používá Booleovskou logiku, při rozhodnutí, která zpráva má být doručena. @ActivationConfigProperty( propertyName=“messageSelector”, propertyValue=“MessageFormat = ‘Version 3. 4’”),

Message properties jsou dodatečné headers (hlavičky), které umožňují, aby dodavatelé připojili informace ke zprávě, které nejsou součástí těla zprávy. Vlastnosti (properties) mohou být typu řetězec, nebo některý z primitivních typů. U beanu ReservationProcess se používá message selector, filtrující zprávy specifického formátu. Jedná se o formát “Version 3.4”; což je řetězec, který Titan používá k identifikaci zpráv typu MapMessage, který obsahuje jmenné hodnoty CustomerID, CruiseID, CreditCard a Price. Přidáním MessageFormat ke každé rezervační zprávě umožňuje psát MDBs, které jsou navrženy ke zpracování rozdílných typů rezervačních zpráv. Následuje, jak JMS producent nastaví vlastnost MessageFormat ve zprávě: Message message = session.createMessage(); message.setStringProperty(“MessageFormat”,”Ver sion 3.4”);

Page 107: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

107

// set the reservation named values sender.send(message);

Acknowledge mode Termín acknowledgement (potvrzení) znamená, že klient JMS oznámí poskytovateli JMS (message routeru), když obdrží zprávu. V EJB je to kontejner MDB, který je zodpovědný za poslání potvrzení (acknowledgementu) když obdrží zprávu. Potvrzení zprávy řekne poskytovateli JMS, že kontejner MDB přijal a zpracoval zprávu. Mode acknowledgement se nastaví použitím standardní aktivační konfigurační property acknowledgeMode. @ActivationConfigProperty propertyName=“acknowledgeMode”, propertyValue=“Auto-acknowledge”)

PropertyValue může nabývat dvě hodnoty:

• Auto-acknowledge – říká kontejneru, že by měl poslat acknowledgement poskytovateli JMS brzy poté, co je zpráva daná instanci MDB ke zpracování. Vyhýbá se duplikování zpráv.

• Dups-ok-acknowledge – říká kontejneru, že nemusí poslat potvrzení o obdržení zprávy okamžitě; kdykoli poté, co je zpráva daná instanci MDB to bude dobré.

Životní cyklus beanů řízených zprávami Životní cyklus má dva stavy:

• Does Not Exist – v tomto stavu ještě nebyla vytvořena instance

• Method-Ready Pool – instance MDB přejde do tohoto stavu, když to potřebuje kontejner.

Obr. 9.1 Životní cyklus beanů řízených zprávami

Does Not Exist

Method -Ready Pool

business method

@PreDestroy

Page 108: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

108

Když je doručena zpráva MDB, ta je delegovaná libovolné volné instanci v Message-Ready Pool. Pokud instance vykonává požadavek, nemůže zpracovávat další zprávu. MDB může zpracovávat mnoho zpráv současně tím, že deleguje zodpovědnost za zpracování každé zprávy na jinou instanci MDB. Když kontejner deleguje zprávu na instanci, MessageDrivenContext instance se změní aby odrážel kontext nové instance. Poté co instance skončí zpracování, je okamžitě dostupná ke zpracování nové zprávy. MDB instance opouští Method-Ready Pool do stavu Does Not Exist, když o tom rozhodne server (sníží počet instancí). Metoda označená anotací @PreDestroy může vykonávat libovolné úklidové operace jaka např. uzavření zdrojů.

Kapitola o beanech řízených zprávami obsahuje především Java MEssage Service (JMS) a její integraci do technologie EJB. Dále se pak zabývá vývojem těchto beanů a jejich sémantikou.

Page 109: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

109

10 Transakce

V této kapitole se dozvíte: • co jsou to transakce a základní požadavky na ně, • jak se odlišují transakce řízené kontejnerem, beanem a klientem, • co jsou to transakční atributy.

Provedení řady diskrétních operací jako jedné pokračující atomické operace. Příklad – výběr částky z jednoho účtu a její uložení na jiným účet. Chci aby se provedly obě operace dobře, nebo žádná – jinak problém. try { // Withdraw money from account 1 } catch (Exception e) { // If an error occurred, do not proceed. return; } try { // Otherwise, deposit money into account 2 } catch (Exception e) { // If an error occurred, do not proceed, // and redeposit the money back into account 1. return; }

Problémy tohoto přístupu: • kód je objemný a „neohrabaný“ • v každém kroku je třeba zvažovat možný potencionální problém, který může nastat a

rutinu, která opraví chyby pomocí „roll back“ změn. • zpracování chyb se vymkne kontrole v případě vykonávání komplexnějších operací

(obsluha více účtů současně); • problém testování kódu. Ideálně chceme provedení všech operací, nebo žádné. Předpoklad, že logika bankovního účtu je distribuovaná ve více vrstvách rozmístění (deployment). Distribuce aplikace v síti způsobí chyby a problémy spolehlivosti. Co se např. stane při výpadku sítě během bankovních operací. Typicky je vyhozena výjimka (Java RMI RemoteException) pro kód klienta, která však není jednoznačná. Mohla vypadnout síť např. před výběrem peněz z účtu, nebo po výběru peněz z účtu – nedá se to rozlišit. Kromě sítě mohou působit problémy perzistentní informace v databázi. • kolaps samotné databáze, • kolaps počítače na němž je rozmístěna databáze. Je třeba mít „recovery process“, který ošetří uvedené havárie. Předpokládejme, že všechny aplikační servery sdílejí perzistentní data v jedné databázi. Aplikační servery tedy potenciálně mohou modifikovat stejnou množinu dat v databázi. Mohou vznikat irelevantní informace. Nutnost mechanismu, který zvládne řízení souběžně pracujících uživatelů na modifikaci dat databáze.

Page 110: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

110

Obr. 10.1 Aplikační servery a jedna databáze Výhody transakcí • Popsaným problémům se dá vyhnout správným používáním transakcí. • Transakce – řada operací, která se jeví při vykonávání jako jedna velká atomická operace. • Transakce garantují „all-or-nothing value proposition“ (všechny úspěšné nebo žádná

úspěšná). • Správné používání transakcí způsobí, že víceuživatelské interakce s databázemi (nebo

jinými zdroji) se mohou provádět nezávisle. • Transakce navíc umožňují pokročilou formu souběžného (konkurentního) řízení a

zpracování výjimek. Vlastnosti ACID ACID – atomicity, consistency, isolation, durability. Slovník transakce. • Transakční objekt (transakční komponenta) je komponenta aplikace, která se zúčastní

transakce. • Transakční manažer – je zodpovědný za řízení transakčních operací transakční

komponenty. Řídí režijní entity transakce, které pracují za scénou a koordinují věci (dirigent diriguje symfonii).

• Zdroj je perzistentní paměť, ze kterého se dá číst / zapisovat. Manažer zdroje – řídí zdroj např. driver pro relační databázi, objektovou databázi, frontu zpráv. Manažeři zdrojů jsou odpovědni za řízení všech stavů, které jsou permanentní. Nejpopulárnější rozhraní pro komunikaci mezi manažery zdroje a transakčním manažerem je X/Open XA resource manager interface. Atomicity (atomičnost) – garantuje, že mnoho operací může být „svázáno“ spolu a jevit se jako pokračující jednotka práce. Consistency (konzistentnost) – garantuje, že transakce zanechá konzistentní stav systému po svém vykonání.

Client Code

Client Code

Client Code

Client Code

Application Server

Application Server

Application Server

Databáze

Page 111: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

111

Isolation (izolace) – chrání konkurentní vykonávání transakcí tak, aby žádná z nich neviděla nekompletní výsledky jiné. Tarnsakce o sobě navzájem nevědí. • využívají se nízko-úrovňové synchronizační protokoly Durability (trvanlivost) – garantuje, že aktualizované, řízené zdroje přežijí havárie a výpadky. Modely transakcí Nejpopulárnější – flat transaction a nested transaction. Flat Transaction • je to řada operací, které jsou vykonávány automaticky jako jedna jednotka práce (unit of

work). • při ukončení transakce je vždy binární výsledek – success, failure. • úspěšná transakce je provedena (committed), neúspěšná je zrušena (aborted). • když je transakce provedena (committed), všechny perzistentní operace se stanou

permanentními změnami; všechny aktualizace zdrojů jsou trvalé. • při zrušení jsou všechny změny „roll back“. Hlavní důvody zrušení transakce

• Nevhodné parametry předané jedné z komponent. • Porušení neměnného stavu systému – přečerpání účtu. • chyby HW a SW.

Vnořené transakce Vnořené transakce umožňují vnořit atomické jednotky práce do jiných jednotek práce. Vnořené transakce mohou reprezentovat strom transakcí. Kořenová transakce (root) a podtransakce (subtransactions). Další modely:

• zřetězené transakce Distribuované transakce Distribuované transakce jsou transakce zasahující více vrstev rozmístění (deployment) a potenciálně zahrnující více typů zdrojů. Možné scénáře distribuovaných transakcí: • více aplikačních serverů spolupracujících v jedné transakci; • aktualizace různých databází ve stejné transakci; • vykonání aktualizace databáze a poslání, nebo přijetí zprávy JMS z fronty zpráv ve stejné

transakci; • propojení na zděděný systém, jeden nebo více typů zdrojů ve stejné transakci. Trvanlivost a dvoufázový protokol commit • První fáze – začíná zasláním zprávy before commit všem zdrojům zahrnutým v transakci. • V této fázi mají zdroje zahrnuté v transakci poslední šanci tuto transakci zrušit. • Všechny aktualizace zdrojů jsou zaznamenány do transakčních logů nebo journálů. • Druhá fáze nastane pouze proběhne-li fáze jedna bez problémů. Dvoufázový protokol commit V distribuovaném dvou fázovém commitu existuje jedna hlavní transakce nazvaná: distributed transaction coordinator.

Page 112: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

112

Cíle transakčního koordinátora: 1. Rozesílá zprávu prepare to commit každému transakčnímu manažerovi zahrnutém do

dané transakce. 2. Každý transakční manažer může šířit tuto zprávu manažerům zdrojů, kteří jsou ve vazbě s

transakčním manažerem. 3. Každý transakční manažer podává zprávu zpět transakčnímu koordinátoru. Pokud všichni

souhlasí s operací commit, je operace commit v případě krachu logována. 4. Transakční koordinátor řekne každému transakčnímu manageru, aby provedl commit.

Každý transakční manažer volá každého manažera zdroje, který provede aktualizaci permanentní a trvalou. V případě chyby se použije vstup log k opakování posledního kroku.

Deklarativní transakční řízení Deklarativní transakční řízení je implicitní rys Enterprise JavaBeans. Bez tohoto rysu by bylo třeba používat explicitní transakční řízení jako např. Object Transaction Service nebo Java Transaction Service. Transakční chování EJBs může být řízeno s použitím anotace @javax.ejb.TransactionAttribute, nebo samozřejmě popisovačem rozmístění (deployment descriptor). Oba způsoby umožňují nastavit atributy transakce jednotlivých metod. To znamená, že transakční chování EJB může být měněno beze změny business logiky, pouze anotací, nebo XML (popisovačem). Deklarativní transakční řízení zjednodušuje složitost transakcí pro vývojáře EJB aplikací. 10.1 Transakce řízené kontejnerem, beanem a klientem Jakmile je transakce spuštěna, skončí buď commitem nebo abortem.

• Klíčové informace: – kdo začne (spustí) transakci – kdo vydá commit nebo abort – kdy každý z těchto kroků nastane

• Podle toho kdo vymezí hranice transakce rozlišujeme styl řízení transakce a to: – container managed – bean managed – client controlled

Když používáme beanem řízené transakce, poskytovatel beanu (bean provider) je zodpovědný za programování transakční logiky v kódu aplikace. To znamená, že jste to vy, kdo vydá příkaz begin a commit nebo abort. U bankovní aplikace může bean působit jako bank teller. Teller bean vystaví (expose) metody pro převod peněz z jednoho účtu na druhý.

Page 113: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

113

Obr.10.2 Beanem řízená transakce Kontejnerem řízená transakce Tento typ transakce dovolí komponentám, aby byly vloženy do seznamu transakcí. To znamená, že bean nikdy explicitně nevydá příkaz begin, commit nebo abort.

Obr. 10.3 Kontejnerem řízená transakce

EJB Container Client Code

1. Call business method

2. Delegate

Teller Bean

4. Perform buseness operations

Transaction Service

3. Call begin()

5. Call commit or abort

Container classes that implement

session bean’s POJO

EJB Container Client Code

1. Call business method

3. Delegate

Teller Bean

4. Perform buseness operations

Transaction Service

2. Call begin()

5. Call commit or abort

Container classes that implement

session bean’s POJO

Page 114: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

114

Klientem řízená transakce Tato situace nastane pokud transakce začíná a končí z kódu klienta, mimo bean. Např. při použití servletu, JSP tag knihovny, samostatné aplikace nebo apletu.

Obr. 10.4 Klientem řízená transakce Rozsah Transakce (Transacton Scope) Rozsah transakce je základním pojmem v transakčním zpracování. Rozsah transakce odkazuje na beany (session a entitní), které se zúčastní konkrétní transakce. Rozsah transakce začíná, když klient vyvolá metodu bookPassage() TravelAgent beanu. Po zpoštění transakce, je tato dále šířena. Transakce musí být atomická (úspěšná, neúspěšná). Je snadné sledovat rozsah transakce sledováním tzv. vlákna vykonávání (thread of execution). Transakce může být ukončena vyhozením výjimky. Kromě vlákna vykonávání, hrají také důležitou roli atributy transakce. EJ bean se může zúčastnit transakčního rozsahu libovolné „unit of work“ implicitně prostřednictvím EJB’s transakčních atributů, nebo explicitně prostřednictvím JTA. 10.2 Transakční atributy Jako vývojář aplikace – normálně není třeba řídit transakce explicitně. Servery EJB umí řídit transakce implicitně, pomocí transakčních atributů vytvořených v čase rozmístění. Je-li bean rozmístěn, je možné nastavit jeho transakční atribut pomocí anotace @javax.ejb.TransactionAttribute, nebo popisovačem rozmístění na několik hodnot: NotSupported, Supports, Required, RequiresNew, Mandatory, Never

EJB Container

Client Code

2. Call business method

3. Delegate

Teller Bean

4. Perform buseness operations

Transaction Service

1. Call begin()

5. Call commit or abort

Container classes that implement

session bean’s POJO

Page 115: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

115

Transakční atributy je možné nastavit na celou transakci (jednodušší způsob), nebo na jednotlivé metody (flexibilnější způsob). public enum TransactionAttributeType { MANDATORY, REQUIRED, REQUIRES_NEW, SUPPORTS, NOT_SUPPORTED, NEVER } @Target({METHOD, TYPE}) public @interface TransactionAttribute { TransactionAttributeType value() default TransactionAttri buteType.REQUIRED; } @TransactionAttribute m ůže být použit na metodu, nebo na t řídu beanu: import static TransactionAttributeType.*; @Stateless @ReansactionAttribute(NOT_SUPPORTED) public class TravelAgentBean implements TravelAgent Remote { public void setCustomer(Customer cust) { . . . } @TransactionAttribute(Required) public TicketDO bookPassage(CreditCardDO card, d ouble price) { . . . } }

Transakce klienta – suspended znamená, že transakce není šířena (propagovaná) k vyvolané metodě EJB; šíření transakce je dočasně pozastaveno, dokud se metoda nedostane na return. NotSupported

• Vyvolání metody enterprise beanu suspenduje transakci dokud není metoda dokončena.

Supports • Atribut znamená, že metoda enterprise beanu bude začleněna do transakčního rozsahu,

je-li vyvolána během transakce. Required

• Atribut znamená, že metoda beanu musí být vyvolána uvnitř transakčního rozsahu. • Je-li volající klient nebo bean částí transakce, bean s atributem required je

automaticky začleněn do rozsahu transakce. • Není-li volající klient nebo bean začleněn do transakce bean s atributem required

začne svoji novou transakci. • Rozsah nove transakce však zahrnuje pouze bean s atrubutem required a všechny jeho

následné beany. RequiresNew

• Atribut znamená, že se vždy odstartuje nová transakce. • Nezáleží na tom, zda je volající klient, nebo bean částí transakce, metoda s uvedeným

atributem začne při vyvolání novou transakci.

Page 116: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

116

• Je-li volající klient již zahrnut do transakce, je tato transakce suspendována, dokud není u nove transakce dosaženo konce.

Mandatory

• Atribut znamená, že metoda beanu musí být vždy částí transakčního rozsahu volajícího klienta.

• Bean nesmí startovat svoji vlastní transakci; transakce musí být šířena od klienta. • Pokud volající klient není částí transakce, vyvolání skončí chybou a vyhozením

výjimky. Never

• Atribut znamená, že metoda beanu nesmí být vyvolána v rozsahu transakce. • Je-li volající klient nebo bean částí transakce, bean s atributem never vyhodí výjimku. • Pokud volající klient nebo bean není zahrnut do transakce, bean s atributem never

bude vykonán normálně bez transakce. Perzistence EJB 3.0 a transakční atributy • Specifikace EJB silně doporučuje přístup v rozsahu JTA transakcí prostřednictvím

EntityManageru. • Pokud máte přístup k perzistentním entitám, používejte pouze transakční atributy:

Required, RequiredNew a Mandatory. • Toto omezení zabezpečí, že všechny přístupy do databáze budou pouze v kontextu

transakce, což je důležité když kontejner řídí automaticky perzistenci. Propagace transakcí

• Ukázka příkladu na metodě bookPassage() beanu TravelAgent. • Defóltní transakční atribut Required. • Commit učiní požadované změny permanentními. • Pokud např. entita Reservation nemůže být vytvořena Entitním Managerem, změna

vytvořené beanem ProcessPayment je „odvalena zpět“. • Je třeba zabránit nekonzistentním datům v databázi. • V případech, kdy kontejner řídí implicitně transakci, rozhodnutí o „commit“ nebo „roll

back“ jsou automaticky v působnosti kontejneru. • Je-li transakce explicitně řízena beanem, nebo klientem jsou uvedená rozhodnutí na

nich. Předpokládejme, že bean TravelAgent je vytvořen a používán klientem následovně: TravelAgent agent = (TravelAgent)jndi.lookup(“Trave lAgent”); agent.setCabinID(cabin_id); agent.setCruiseID(cruise_id); try { agent.bookPassage(card, price); } catch(Exception e) { System.out.println(“Transaction failed! “); }

Metoda bookPassage() má transakční atribut RequiresNew

• v tomto případě, klient, který vyvolá bookPassage() není součástí transakce. Když je bookPassage() vyvolána TravelAgent beanem, je vytvořena nová transakce. To znamená, že bean TravelAgent se registruje u transakčního manažéra, který bude řídit transakci automaticky.

Page 117: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

117

Když je vyvolána metoda creditBy() uvnitř metody bookPassage(), bean ProcessPayment se registruje u transakčního manažera pod transakčním kontextem, který byl vytvořen pro bean TravelAgent; transakční kontext je šířený k beanu ProcessPayment.

Obr. 10.5 Řízení transakčního kontextu TravelAgent beanu Zámky databáze Read locks

• zámek zabraňuje jiným transakcím měnit čtená data, dokud transakce neskončí; jiné transakce mohou data také číst;

Write locks • zámek zabraňuje jiným transakcím měnit data dokud daná transakce neskončí; naopak

dovoluje jim dirty read Exclusine write locks

• zámak zabraňuje jiným transakcím číst nebo měnit data, dokud transakce neskončí; zabraňuje dirty read

Snapshots • je to zamrzlý pohled na data, který je proveden při začátku transakce.

Problém Dirty Read Dirty problem – aplikace čte data z databáze, která ještě nebyla „committed“ ke zdroji.

1. Čtete X z databáze. Aktuálně X=0. 2. Přičtete 10 k X a uložíte do DB. X=10, ale neproběhl commit. 3. Jiná aplikace načte X (X=10). 4. Zrušíte transakci – X=0. 5. Jiná aplikace přičte 10 k X a uloží do databáze, kde X=20 ??

Dirty read nastává v módu READ UNCOMMITTED

Client Application

Transaction Manager

TravelAgent EJB Reservation entity

ProcessPayment EJB

Process- Payment

EJB

TravelAgent EJB

Persistence context

Invoke bookPassage()

RequiresNew

Required

Required

Register the TravelAgent EJB

Invoke byCredit()

Register the ProcessPayment EJB

TransactionalContext X

Register the Reservation entity

Create a new Transactional Context called X. Add the TravelAgent EJB to X.

Add the Reservation entity to X

Add the ProcessPayment EJB to X

Invoke EntityManager.persist() (reservation)

1

7 8

10

11

2

3 6

9

4

5

Page 118: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

118

Je nevhodné používat tento izolační mód při zpracování citlivých kalkulací jako bankovních účtů. Použití jen za předpokladu samostatného běhu instance dané komponenty. Výhoda – rychlost provedení, nejsou potřebné žádné zámky. Mód READ COMMITTED bude číst pouze data, která jsou již „committed“. Nikdy se nebudou číst data, která byla zapsaná do databáze, ale nejsou committed. Výhodné použití pro čtení dat z DB pro „reporting values“. Výkonnost je pomalejší než u READ UNCOMMITTED z důvodů dalšího zámku. Fantomův problém Phantom je nová množina dat, která se v databázi objeví během dvou operací čtení. Např.

1. Vaše aplikace se dotazuje v databázi pomocí kritéria a získá množinu dat. 2. Další aplikace vloží nová data vyhovující výběrovému kritériu. 3. Provedete opakovaný výběr a množina vybraných dat je změněna.

Návrh transakčních konverzí v EJB Neúspěšné transakce automaticky vyvolají návrat na bod před transakcí (aktualizací), ale aktualizace DB je pouze polovina akce. Druhou polovinu tvoří kód aplikace, která se musí vypořádat s dopady transakce. Pro bezstavové session beany je zrušení business procesu jednoduchá úloha – vyhodí výjimku zpět ke klientovi. Stavové session beany reprezentují proces, který překrývá řadu volání metod a proto má v paměti konverzační stav. Naštěstí dobře navržený stavový session bean může, v případě neúspěšné transakce, zachránit svůj konverzační stav. Klíčem je navrhnout beany tak, aby si uvědomovaly změny v konverzačním stavu a byly schopné „undo“ operace v případě neúspěšné transakce. Tato činnost je však příliš aplikačně závislá, než aby to dělal aplikační server za vás. Aplikační server pomůže pouze při stanovení, kdy byla transakce neúspěšná. Třída stavového session beanu musí proto implementovat volitelné rozhraní javax.ejb.SessionSynchronization s následujícím kódem: public interface javax.ejb.SessionSynchronization { public void afterBegin(); public void beforeCompletion(); public void afterCompletion(); }

Uvedené metody je třeba implementovat a kontejner je bude vyvolávat v případě neúspěchu.

• afterBegin() je vyvolána kontejnerem přímo po začátku transakce. • beforeCompletion() je vyvolaná kontejnerem bezprostředně před ukončením

transakce. • afterCompletion() je vyvolaná kontejnerem přímo po ukončení transakce.

Nejdůležitější bývá metoda afterCompletion(). Kontejner ji vyvolá vždy ať již po commit nebo abort. Metoda afterCompletion() má parametr boolean, kterým je možné rozřešit, zda transakce byla úspěšná či nikoli

• true - úspěšná

Page 119: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

119

Transakční mechanismus je rovněž velmi důležitý pojem v technologii EJB. Po úvodu se kapitola věnuje především distribuovaným transakcím a jejich specifikám. Rozebírá transakční řízení kontejnerem, beanem a klientem. Popisuje využití transakčních atributů.

Page 120: Distribuované programování s prvky komponent XDPKOhunka/vyuka/javaOOP/XXDPKOM.pdf · 1 Distribuované programování s prvky komponent XDPKO Doc. Ing. František Hu ňka, CSc.

120

Doporučená studijní literatura 1. Monson-Haefel R.: Enterprise JavaBean 3.0. O’Reilly 2006

2. Sriganesh, P.R., Brose, G., Silverman, M.: Mastering Enterprise JavaBeans 3.0. Wiley 2006 http://wwwtheserverside.com/tt/books/wiley/masteringEJB3/index.tss

3. http://java.sun.com/javaee/5/docs/tutorial/doc/

4. http://www.jboss.org

5. Alur, D., Malks, D., Crupi, J.: Core J2EE Patterns: Best Practices and Design Startegies, Prentice Hall PTR. 2003 (EJB 2.1) http://www.theserverside.com/books/wiley/EJBDesignPatterns/index.tss

6. Zelený, J., Nožička. J.: COM+ CORBA EJB. BEN Praha 2002


Recommended