COOKBOOK EJB 3.0
Martijn Blankestijn
Ordina J-Technologies
Cookbook EJB 3.0
Ordina J-Technologies
Cookbook EJB 3.0
Inhoudsopgave Inhoudsopgave 1. Inleiding
3 6
1.1. Publiek 6 1.2. Doelstelling 6 1.3. Opbouw �� 6 2.
Enterprise Java Beans Architecture
8
2.1. Introductie 8 2.2. Typen �� 8 2.2.1. Stateful Session Enterprise Java Bean 9 2.2.2. Stateless Session Enterprise Java Bean 9 2.2.3. Message Driven Enterprise Java Bean��� 9 2.3. Services �� 9 2.3.1. Concurrency 9 2.3.2. Transaction management 10 2.3.3. Object distributie 11 2.3.4. Resource dependencies 12 2.3.5. Security 12 2.3.6. Persistentie���� 12 2.4. Beperkingen in het programmeer model���� 12 3.
Implementatie van Services
14
3.1. Eerste business component 14 3.2. Concurreny 15 3.3. Transaction Management ��� 16 3.3.1. Bean-managed transactions ��� 16 3.4. Object distributie 17 3.4.1. Performance 17 3.5. Resource dependencies 18 3.5.1. Dependency Injection 18 3.5.2. Naming 19 3.6. Security 21 3.7. Message Driven Beans 21 3.8. Stateful Session EJB 23 3.8.1. Verschil tussen 2.1 en 3.0 24 3.9. Overig 25 3.9.1. Callbacks 25 3.9.2. Excepties��� 26
Ordina J-Technologies
4.
Cookbook EJB 3.0
Persistentie met de Java Persistence API
27
4.1. Inleiding 27 4.2. Eerste persistente entiteit 28 4.2.1. Entiteitklasse 28 4.2.2. Aanmaken Persistence Unit 29 4.2.3. EJB met JPA 30 4.3. JPA binnen Java SE 31 4.4. Associaties 33 4.5. Levenscyclus van een entiteit 35 4.5.1. Nieuw object 35 4.5.2. Object uit database 36 4.5.3. Detached objects and merging 36 4.6. Transacties 37 4.7. Optimistic locking 38 4.8. Java Persistence Query Language 39 4.9. Mapping 40 4.9.1. Embeddable objects 40 4.9.2. Enumeraties 41 4.9.3. Mapping files 41 4.10. Wat zit niet in de Java Persistence API? 42 4.10.1. Caching 42 4.10.2. Criteria 42 4.10.3. Pessimistic Locking 42 4.11. JPA en Spring ��� 43 5.
5.1. 5.2. 5.3. 5.4. 5.5. 5.6. 6.
Interceptors
44
Use cases 44 Interceptor voorbeeld 44 Koppelen van interceptor 45 Enterprise Naming Context 46 Levenscyclus 46 Waarschuwing ��� 47 Assembly
48
6.1. Deployment Descriptor 48 6.2. Packaging���� 48
Ordina J-Technologies
7.
Best practices
Cookbook EJB 3.0
50
7.1. Core EJB 50 7.1.1. Prefereer local interfaces boven remote interfaces 50 7.1.2. Annotaties op EJB interfaces 50 7.1.3. Interceptor 50 7.1.4. Deployment descriptors 51 7.2. JPA 51 7.2.1. Annotaties versus mapping file 51 7.2.2. EJB 2.1 style Entity Beans 52 7.2.3. Packaging entities 52 7.2.4. Primitive type of Wrapper 52 7.2.5. Naamgeving Named queries 52 7.2.6. Named parameters 53 7.2.7. Constructor 53 7.2.8. Field versus property accesstype 53 7.3. Performance ��� 54 8.
Gotcha’s
55
8.1. lib-directory in enterprise application 55 8.2. Persistentie 55 8.2.1. Introductie nieuwe persistence unit 55 8.2.2. Date en Calendar velden 56 8.2.3. Relaties 56 8.2.4. Gemanagede objecten 56 8.2.5. Gebruik Java 2 Standard Edition 57 8.2.6. Detached entities en lazy loading ��� 57 9.
Software en Bronnen
59
9.1. Gebruikte software 59 9.2. Geraadpleegde bronnen ��� 60 10.
Bijlagen
61
Ordina J-Technologies
Cookbook EJB 3.0
1. Inleiding Enterprise Java Beans is een server-side component framework. Het is bedoeld om de ontwikkeling van Enterprise Applications eenvoudiger te maken door een aantal standaard services aan te bieden aan ontwikkelaars. Services op het gebied van transacties, gedistribueerde objecten en beveiliging nemen de ontwikkelaar veel complex, technisch werk uit handen, zodat de focus meer kan liggen op de ontwikkeling van business functionaliteit. Dit document geeft een laagdrempelige introductie van de laatste EJB specificatie, versie 3.0.
1.1. Publiek Dit document is bedoeld voor Java ontwikkelaars die geen ervaring hebben met de ontwikkeling van Enterprise Java Bean componenten.
1.2. Doelstelling Doelstelling van dit document is om ontwikkelaars die willen beginnen met EJB 3.0 een handreiking te geven bij het opzetten, inrichten, ontwikkelen, testen en deployen van EJB 3.0 componenten.
1.3. Opbouw Als eerste zal in hoofdstuk 2 een korte introductie over Enterprise Java Beans gegeven worden. Hier worden onder andere het doel, de verschillende typen en de standaard services besproken worden. Dit wordt gevolgd in hoofstuk 3 door voorbeelden van de verschillende typen componenten en de services. In hoofdstuk 4 wordt de Java Persistence API (JPA) behandeld, de standaard manier om binnen Java EE objecten te persisteren naar een (relationele) database. Daarna wordt in hoofdstuk 5 de (beperkte) introductie binnen EJB 3.0 van Aspect Orient Programming (AOP) in de vorm van interceptors toegelicht en in welke situaties interceptors een oplossing kunnen betekenen. In hoofdstuk 6 wordt het samenstellen van een Enterprise Application behandeld. In hoofdstuk 7 worden best practices voor EJB 3.0 behandeld en in hoofdstuk 8 komen een
Ordina J-Technologies
Cookbook EJB 3.0
aantal gotcha’s aan bod, waar je makkelijk tegen aan kunt lopen. Vervolgens komen in bijlagen achtereenvolgens de inrichting van het project dat gebruikt wordt in de voorbeelden aan bod en de huidige stand van zaken rond tooling en applicatie servers in de markt.
Ordina J-Technologies
Cookbook EJB 3.0
2. Enterprise Java Beans Architecture 2.1. Introductie De officiele definitie [EJB3] luidt als volgt: The Enterprise JavaBeans architecture is a component architecture for the development and deployment of component-based distributed business applications. Kernpunt is dat het een architectuur is voor componenten om gedistribueerde applicaties mee te maken. Het is een verplicht onderdeel van de J(2)EE-specificatie, waaraan leveranciers van J(2)EE-applicatieservers moeten voldoen. Met de EJB 3.0 specificatie zijn een aantal fundamentele wijzigingen aangebracht in het programmeermodel van Enterprise Java Beans. Wat belangrijk is om te beseffen is dat de kern van het framework en de architecturele principes nog gelijk zijn. In paragraaf 2.2 zullen drie verschillende typen componenten worden besproken, te weten stateless session beans, stateful session beans en message driven beans en hun toepassingsgebieden. een vierde type component is de Entity bean. Deze componenten moeten nog wel worden ondersteund door JEE 5 applicatieservers, maar zijn door de introductie van de Java Persistence API feitelijk obsolete geworden. Naar alle waarschijnlijkheid zullen Entity Beans in één van de volgende versies van de specificatie gaan verdwijnen. Een van de doelstellingen van de EJB-architectuur is het vereenvoudigen van de ontwikkeling van business components (EJB’s). Ontwikkeling wordt eenvoudiger als de ontwikkelaar geen low-level kennis meer hoeft te hebben van de details van transactie en state management, multi-threading, connection pooling, security etc. Dit is mogelijk, omdat in de specificatie een aantal standaard services zijn gedefinieerd die verplicht zijn. Deze services wordt in paragraaf 2.3 behandeld.
2.2. Typen De verschillende typen server-side componenten die bestaan, zijn toegesneden op verschillende communicatie/conversatie stijlen: • • •
stateful session bean stateless session bean message driven bean
Ordina J-Technologies
Cookbook EJB 3.0
2.2.1. Stateful Session Enterprise Java Bean Dit type EJB is bedoeld om de state ten behoeve van één specifieke client (denk aan een gebruiker van een webapplicatie) bij te houden. Deze EJB wordt gebruikt op het moment dat er een ‘conversatie’ is met de client, waarbij bijvoorbeeld op het einde van de conversatie pas zaken bewaard/doorgevoerd hoeven te worden in achterliggende systemen/ componenten. Het meest bekende voorbeeld is het online winkelmandje.
2.2.2. Stateless Session Enterprise Java Bean Dit type is bedoeld voor synchrone services, waarbij geen state ten behoeve van een client wordt bijgehouden. Stateless EJB’s worden door zo ongeveer elke container gepooled 1, waardoor bij twee aanroepen via dezelfde referentie twee verschillende EJB-instanties gebruikt kunnen worden door de container.
2.2.3. Message Driven Enterprise Java Bean Dit type EJB is bedoeld voor het verwerken van asynchrone berichten. De code van een message driven bean wordt uitgevoerd bij de ontvangst van een bericht. De message driven bean houdt net als de stateless session bean geen state bij ten behoeve van een client.
2.3. Services Een ander belangrijk onderdeel van de EJB- en Java EE specificatie zijn de standaard services die container-leveranciers moeten bieden aan de componenten. Deze services maken het mogelijk voor de ontwikkelaar om zich meer te concentreren op ontwikkeling van business logica dan op onderliggende plumbing 2 code.
2.3.1. Concurrency Concurrency 3 is een belangrijke service binnen de EJB-architectuur. Deze service maakt één van de complexere gebieden van software ontwikkeling een stuk eenvoudiger. De toegang tot Session bean instanties worden afgeschermd door de container. De container zorgt er voor dat één instantie nooit tegelijkertijd twee aanroepen tegelijkertijd moet verwerken. In die zin zijn Session beans dus thread safe. Tijdens ontwikkeling van een component hoeft geen rekening te houden met synchronisatie van de methoden van een EJB-instantie. Dit wordt immers door de container geregeld. 1 het aanhouden van een groep van instanties die kunnen worden hergebruikt voor verschillende client requests. 2 plumbing ofwel leidingenwerk. Code die geen business functionaliteit uitdrukt, maar vanwege technische redenen nodig is. 3 gelijktijdige verwerking van meerdere requests
Ordina J-Technologies
Cookbook EJB 3.0
2.3.2. Transaction management Eén van de belangrijkste features van de de EJB-architectuur is de ondersteuning van gedistribueerde transacties. De EJB-architectuur maakt het mogelijk dat een ontwikkelaar een applicatie maakt die verschillende transactionele resources atomair update. Belangrijk om je te realiseren bij het ontwerpen en het werken met transacties is de scope van een transactie. De scope van een transactie kan zich uitstrekken over meerdere EJB’s en meerdere resource-managers (databases, messaging systemen). Dit wordt ook wel transaction context propagation (verspreiding,voortplanting) genoemd. Dit betekent dat meerdere methodes dezelfde transactie-context hebben en dus ook allen zullen slagen (commit) of falen (rollback). De EJB Container werkt hier als component-transactie-monitor en coordineert de resource managers (zie [GOETZ]). In de specificatie worden alleen enkelvoudige transacties onderkend. Geneste transacties, waarbij een transactie wordt opgestart binnen de context van een al lopende transasctie, worden niet door de specificatie beschreven. Als een EJB die altijd een nieuwe transactie opstart, wordt aangeroepen in de context van een lopende transactie, dan wordt de lopende transactie gesuspend totdat de nieuwe transactie afgelopen is. Het transactionele gedrag van een methode van een EJB wordt uitgedrukt door middel van een ‘transactie-attribuut’. De ondersteunde transactie-attributen zijn: • verplicht (mandatory) De EJB-methode moet aangeroepen worden binnen de context van een lopende transactie. Wordt de methode zonder transactie aangeroepen, dan gooit de container een exceptie. • vereist (required) Hier is een transactie vereist. Als de EJB-methode niet in de context van een transactie is aangeroepen, start de container een transactie. • vereist nieuwe (requiresNew) Dit betekent dat er altijd een nieuwe transactie wordt gestart door de container. Als de client van de EJB al een transactie gestart had, wordt deze tijdelijk opgeschort (suspended). • ondersteund (supports) Dit betekent dat als de EJB-methode wordt aangeroepen binnen de context van een lopende transactie, de methode onderdeel wordt van die transactie. Als geen transactie aanwezig is, wordt geen nieuwe transactie opgestart • niet ondersteund (not supported) Dit betekent dat de EJB-methode niet uit gevoerd wordt binnen een transactie-context en dat de eventuele transactie die gestart is door de client wordt opgeschort. • nooit (never) Dit betekent dat de EJB-methode nooit binnen de context van een lopende transactie mag worden aangeroepen. Gebeurt dit wel dan gooit de container een exception.
10
Ordina J-Technologies
Cookbook EJB 3.0
Er zijn twee ‘smaken’ om transacties te managen: bean-managed transactions (BMT) en container-managed transactions (CMT). In het ene geval is de ontwikkelaar van de EJB verantwoordelijk voor het beginnen en beëindigen van de transactie. Bij container-managed transactions wordt de transactie door de container gemanaged. Default worden transacties gemanaged door de container. Nadeel van BMT is dat de ontwikkelaar verantwoordelijk is voor het starten en, vaak belangrijker, het correct beëindigen van de transactie. Dit geldt des te meer als transacties meerdere aanroepen van business methoden betreffen. Bean managed transactionmanagement kan echter de performance verbeteren als EJBmethoden relatief dure operaties bevatten, die niet specifiek onderdeel hoeven te zijn van de lopende transactie. Een andere mogelijkheid is het gebruik van BMT voor langlopende transacties.
2.3.3. Object distributie Distributie van objecten over verschillende machines is een andere belangrijke service van de EJB architectuur. Object distributie verschaft de mogelijkheid om functionaliteit te verdelen over meerdere machines, waarbij het transparant is voor de client waar de betreffende functionaliteit draait. Voor clients die componenten willen benaderen over het netwerk kan door het component een zogenaamde remote client view beschikbaar worden gesteld: een remote interface (via RMI over IIOP) of een endpoint interface (via SOAP). Deze remote client view maakt het mogelijk om het component over het netwerk te benaderen. Met name door de EJB-to-SOAP mapping is het mogelijk om business logica vormgegeven in een EJB te ontsluiten voor een hele reeks van clients in verschillende talen en platformen. Het is belangrijk te beseffen dat object distributie altijd zijn prijs heeft. Voor de locatie transparantie wordt minimaal de prijs betaald van serialisatie van alle objecten. Op het moment dat ook een network roundtrip wordt gemaakt wordt dit factoren duurder. Lokale interfaces hebben dit nadeel niet. Deze aanroepen worden direct binnen dezelfde Java Virutal Machine uitgevoerd zonder serialisatie van de parameters. Het gaat te ver om dit onderwerp te behandelen in dit cookbook, maar uitgangspunt voor de keuze van object distributie zou Martin Fowler’s first law of distributed object design [PEAA] moeten zijn: Don’t distribute. Oftewel, wees je zeer bewust van de kosten die object distributie met zich mee brengt en maak de keuze voor object distributie als de voordelen opwegen tegen deze performance penalty. In dat geval zijn EJB’s een krachtig middel om gedistribueerde componenten mee te creëren.
11
Ordina J-Technologies
Cookbook EJB 3.0
2.3.4. Resource dependencies Binnen de EJB architectuur is veel ruimte ingebouwd voor het gestandaardiseerd ontsluiten van en managen van resources. Denk bij resources aan connecties naar de database, messaging systemen, mail server e.d., maar ook aan URL’s, Web services en queues. De configuratie van deze resources varieert vaak per omgeving. Bijvoorbeeld in een OTAP-straat. De EJB architectuur zorgt er voor dat dit soort informatie los van de daadwerkelijke applicatie geconfigureerd kan worden, zodat een applicatie geen wijzigingen hoeft te ondergaan bij overgang van de éne naar de andere omgeving.
2.3.5. Security De EJB architectuur brengt ook vereenvoudigingen aan in het beveiligen van de applicatie. Het biedt services op het gebied van authenticatie, autorisatie, geheimhouding en integriteitsbescherming. Security is een heel groot onderwerp. In dit cookbook wordt alleen ingegaan op hoe je gebruik kunt maken van het autorisatiemechanisme van EJB’s en niet op de totale beveiliging van een applicatie.
2.3.6. Persistentie In de nieuwe EJB 3.0 specificatie is de breuk met het verleden duidelijk. Naast het entity bean model, wordt een hele nieuwe manier van persistentie geintroduceerd: de Java Persistence API (JPA). Dit persistentiemodel gaat niet meer uit van componenten, maar bestaat uit Plain-OldJava-Objects (POJO’s). In hoofdstuk 4 wordt hier uitgebreid op ingegaan.
2.4. Beperkingen in het programmeer model Ook voor de ontwikkeling van Enterprise application met behulp van Java EE gaat het gezegde “Elk nadeel heb z’n voordeel.” op. Er zijn grote voordelen te behalen door gebruik van de gestandaardiseerde services in de EJB-architectuur. Deze services worden aangeboden in een gemanagede omgeving (de EJB-container). Binnen deze gemanagede omgeving is het gebruik van een aantal constructies die wel aanwezig zijn binnen de standaard editie van Java verboden/afgeraden om de applicatieserver ook daadwerkelijk zijn resources te laten beheren en de stabiliteit van de server als geheel te garanderen.
12
Ordina J-Technologies
Cookbook EJB 3.0
Zo is het onder andere verboden dat EJB’s zelf threads opstarten, expliciete thread synchronisatie doen en statische velden gebruiken voor lees- en schrijfacties. Deze conflicteren met de concurrency en resource management services die geboden worden door de container.
13
Ordina J-Technologies
Cookbook EJB 3.0
3. Implementatie van Services Zoals behandeld in 2.3 (Services) zijn er een aantal services die door de container worden aangeboden. Deze services zijn: • • • • • •
Concurrency Transaction Management Object distributie Naming Security Persistentie
Doel van dit hoofdstuk is het uitwerken van een voorbeeldcasus, waarbij de verschillende services met praktische voorbeelden worden verduidelijkt. De casus betreft een cursussysteem. In dit systeem kunnen mensen zich inschrijven voor cursussen en kan het aanbod van cursussen onderhouden worden. In de eerste paragraaf wordt een simpel business component ontwikkeld: een stateless session bean. Dit wordt in de volgende paragrafen uitgebreid met services, zoals die in paragraaf 2.3 zijn besproken. Alleen voor persistentie wordt een uitzondering gemaakt. Over deze service is zoveel te bespreken dat dit in een apart hoofdstuk behandeld wordt. De laatste paragrafen zullen de overige typen behandelen: de stateful session en message driven bean. Het opzetten van de ontwikkelomgeving en inrichting van het project is in de bijlagen beschreven.
3.1. Eerste business component We starten met de interface van de cursus-service die functionaliteit biedt voor het opvragen van cursussen. De gerefereerde Course-klasse is een eenvoudig java-object dat alleen bestaat uit attributen en getters en setters.
14
Ordina J-Technologies
Cookbook EJB 3.0
Deze interface lijkt in weinig opzichten op de Remote of Local Interface zoals die gebruikt werd in eerdere specificaties. Er is geen overerving nodig, geen EJB-framework specifieke excepties. Het is een gewone java-interface zonder enige ‘opsmuk’. De implementatie van dit component ziet er als volgt uit:
Het enige dat deze klasse onderscheidt van een gewone java-klasse is de annotatie @ Stateless. Deze specifieke annotatie geeft aan dat deze klasse een Stateless Session Enterprise Bean is. Gebruik van deze EJB vanuit een servlet in de weblaag ziet er met gebruik annotaties als volgt uit:
NOTE: In dit voorbeeld wordt gebruik gemaakt van dependency injection voor het verkrijgen van de referentie naar het component. Dependency injection wordt in een paragraaf 3.5.1 uitgewerkt.
3.2. Concurreny Zoals besproken in 2.3.1 (Concurrency) wordt concurrency geregeld door de EJB-container. Hierdoor hoeven methoden van een EJB niet gesynchroniseerd te worden. Sterker nog: de EJB specificatie verbiedt zelfs het gebruik van synchronized om thread synchronisatie af te dwingen. Daarnaast verbiedt de specificatie ook het creëren van threads binnen een EJB. Achterliggende reden is dat de EJB-container verantwoordelijk is voor de beschikbaarheid van services. Deze verantwoordelijkheid kan alleen door de EJB container worden vervuld, zolang deze de volledige controle heeft over die services.
15
Ordina J-Technologies
Cookbook EJB 3.0
3.3. Transaction Management Zonder in het eerdere voorbeeld iets te hebben toegevoegd aan de EJB’s, zijn ze direct al transactioneel. Dit komt door de default-instellingen die worden voorgeschreven door de EJB specificatie. De default transactiekenmerken van methoden van een EJB zijn namelijk: • Container Managed Transactions Dit houdt in dat de EJB-container de transacties regelt. Als ontwikkelaar hoef je hier dus niet voor te doen. • TransactieAttribute is Required Voor uitleg van de transactie attributen, zie 2.3.2. Voor andere transactie-attributen (zie 2.3.2) kunnen annotaties worden gebruikt.
In dit geval zal bij elke aanroep voor het toevoegen van een cursus altijd een nieuwe transactie worden gestart en zal de transactie die door de client is gestart opgeschort (suspended) worden. Hou rekening met de transactionele karakteristieken van de funcionaliteit. Voor de meeste situaties waar gewoon gebrowsed wordt, is het verplichte transactie-attribuut overkill. Vanuit performance optimalisatie moet het ‘laagste’ transactie-attribuut gekozen worden die de dataintegriteit garandeert.
3.3.1. Bean-managed transactions In het uitzonderlijke geval dat gekozen wordt om de EJB zelf zijn transacties te laten managen in plaats van de container, wordt dit als volgt uitgedrukt:
Binnen de EJB-klasse zal dan expliciet de transactie gestart en beeindigd moeten worden
16
Ordina J-Technologies
Cookbook EJB 3.0
3.4. Object distributie Default is bij EJB’s geen sprake van object distributie. De default client view is local. Dit betekent dat de client en de EJB zich in dezelfde JVM-instantie moeten bevinden. Door middel van de annotatie @Remote kunnen services van een EJB ter beschikking worden gesteld aan een remote client. Dit kan door op twee manieren. Enerzijds door annotatie van de interface:
Anderzijds door annotatie op de EJB-klasse met daarin een verwijzing naar een interface van de bean:
Deze laatste oplossing heeft de voorkeur ( zie ook hoofdstuk 7).
3.4.1. Performance Eén van de belangrijke verschillen tussen lokale en remote interfaces is de omgang met parameters. Bij remote interfaces worden alle parameters gekopieerd (pass-by-value). Als de client en server in verschillende Java Virtual Machines opereren worden de object ook geserialiseerd. De client- en de serverkant hebben en werken met hun eigen kopie van de objectenstructuren. Bij lokale interfaces worden de referenties van de parameters doorgegeven (pass-byreference). Wijzigingen die aan server-kant worden aangebracht zijn zichtbaar voor de cliënt en andersom. Het overgaan van lokale op remote interfaces of andersom kan dus leiden tot onverwacht gedrag als parameter- of returnwaarden worden gewijzigd door de client- of servergedeelte van de EJB. Samenvattend is het gebruik van remote interfaces langzamer dan lokale interfaces door: • Kopiëren van parameters • Serialisatie • Netwerk vertraging (network latency) • Client/Server stack overhead
17
Ordina J-Technologies
Cookbook EJB 3.0
3.5. Resource dependencies Een EJB doet zijn werk meestal niet in isolatie. Hij is afhankelijk van andere bronnen zoals connecties naar databases, andere EJB’s, queues en dergelijke. De Enterprise Bean Environment is het mechanisme dat het mogelijk maakt om afhankelijkheden naar externe bronnen aan te passen tijdens het samenstellen of deployen van de applicatie. In de specificaties voor EJB 3 was dit mogelijk via JNDI (Java Naming and Directory Interface). Met EJB 3.0 is een veel krachtiger mechanisme geintroduceerd, namelijk Dependency Injection. Dit mechanisme is gepopulariseerd door onder andere Spring en Picocontainer. De Naming Service heeft te maken het binden van objecten in een namespace en het terugvinden van deze objecten. Het gebruik hiervan wordt in deze specificatie goeddeels afgeschermd door het gebruik van dependency injection. Onder water wordt echter wel gebruik gemaakt van de naming service voor het opzoeken (resolven) van datgene dat geïnjecteerd moet worden. In deze paragraaf komen achtereenvolgens dependency injection en naming services ter sprake.
3.5.1. Dependency Injection Dependency injection betekent dat de container de afhankelijkheden van een component injecteert. Het component zoekt niet zijn afhankelijkheden op in één of andere registry, maar krijgt zijn afhankelijkheden geïnjecteerd door de container. Deze injectie vind plaats bij de initialisatie van de applicatie en voor de aanroep van een business method. Er zijn twee mogelijke wijze waarop afhankelijkheden geïnjecteerd kunnen worden: • instantievariabele • setter-injection De specificatie ondersteund geen constructor of interface dependency injection (zie [IOCC]). Bij dependency injection via instantievariabele wordt de referentie naar een andere resource of component door de container direct toegekend aan het attribuut:
Bij dependency injection via een settermethode roept de container een setter methode van het component aan:
18
Ordina J-Technologies
Cookbook EJB 3.0
Dit betekent dat bij aanroep van een business method niets meer gedaan hoeft te worden en dat de referentie direct gebruikt kan worden:
Dit is een veel krachtiger mechanisme dan het gebruik van de InitialContext of het ServiceLocator-pattern zoals dat in eerdere versies gebruikelijk was. Niet alleen EJB’s
Het onderliggende mechanisme werkt als volgt als er geen expliciete JNDI-namen gebruikt worden: •
•
•
de default JNDI naam van een remote interface van een component is de fully qualified name van de remote interface (nl.ordina.cookbook.ejb3.payment.service. PaymentService) de container gebruikt de fully qualified name van het type (van de instantievariabele of het argument in de methode) dat door middel van dependency injection gevuld moet worden. In ons voorbeeld is dit de interface PaymentService. De container doet vervolgens een lookup op basis van de fully qualified name van het attribuuttype. de container injecteert deze referentie via een instantievariabele of de settermethode.
3.5.2. Naming Naast dependency injection van de verschillende gemanagede resources zijn er in ieder geval twee situaties denkbaar, waarin expliciet gebruik kan worden gemaakt van de JNDI-API voor het verkrijgen van referenties naar andere resources. Dit is enerzijds vanuit de EJB als er de behoefte is pas at run-time het component op te vragen. Anderzijds op het moment dat er een referentie verkregen moet worden binnen een utility-klasse. In beide gevallen wordt er op de EJB waarbinnen de code wordt uitgevoerd een declaratie toegevoegd van de afhankelijkheid. Deze ziet er als volgt uit:
Dit maakt binnen de EJB Naming Context (ENC) een referentie aan naar een EJB component die de PaymentService interface implementeert onder de naam “ejb/PaymentService”. Het opzoeken van de implementerende klasse van deze interface gaat volgens hetzelfde lookupmechanisme zoals dit in 3.5.1 besproken is.
19
Ordina J-Technologies
Cookbook EJB 3.0
Voor gebruikende klasesn kan dus een referentie naar deze service worden verkregen door het zoeken op dit label middels de JNDI-API:
De SessionContext is het contract van de container met het component. Via deze context kunnen referenties worden opgezocht van andere componenten of resources in de ENC. De tweede manier van gebruik is het opvragen van een ander component of resource binnen een utility klasse. Dependency injection werkt immers alleen maar op klassen die door de Java EE-container gemanaged worden, zoals andere EJB’s, Servlets, JSF managed beans, servlet filters, interceptors etc. Dependency injection werkt niet voor andere klassen. Deze klassen zullen dus via de ‘traditionele’ manier hun afhankelijkheden op moeten zoeken middels de JNDI-API.
NOTE: In utility klassen moet dus nog steeds gebruik worden gemaakt van de InitialContext inclusief de afhandeling van de checked exceptions. Hier moet ook expliciet worden aangegeven dat er binnen de ENC wordt gezocht wat uitgedrukt wordt door het voorvoegsel “java:comp/env”.
20
Ordina J-Technologies
Cookbook EJB 3.0
3.6. Security Security hoeft binnen Java EE niet meer uitgeprogrammeerd te worden, maar kan in veel gevallen declaratief geregeld worden. Het uitdrukken van de regel dat alleen iemand met de rol “Administrator” cursussen mag toevoegen aan het system, kan dit door middel van een annotatie op de methode in de EJBklasse worden toegevoegd:
Deze annotatie is ook mogelijk op het niveau van de klasse. Dan geldt het voor alle methodes op de klasse. Naast RolesAllowed zijn ook de annotaties @PermitAll en @DenyAll mogelijk. De Java EE 5 specificatie laat zich niet uit over hoe een organisatie zijn security architecture moet implementeren. De daadwerkelijke koppeling en definitie van gebruikers en groepen naar rollen is bewust niet gedefinieerd binnen de specificatie. De specificatie voorziet wel in een standaard API (Java Authorization Service Provider Contract for Containers (JACC)) tussen de applicatieserver en de leverancier van de authorisatie service. Dit geeft dus alle vrijheid voor organisaties om een keuze te kunnen maken in het beveiligingsmodel en specifieke technische implementatie (database, LDAP, file, besturingsysteem). Naast JACC schrijft de specificatie ook een standaard API (Java Authentication and Authorization Service (JAASS) voor voor het authenticeren van gebruikers en het handhaven van access control.
3.7. Message Driven Beans Een message driven bean (MDB) is in EJB 3.0 zeer eenvoudig geworden. In het meeste voorkomende geval, waarbij de MDB berichten ontvangen via de Java Messaging Service (JMS), ziet de code er als volgt uit.
De annotatie MessageDriven geeft aan dat het hier een message driven EJB gaat. Bij binnnenkomst van een bericht op de queue zal de container 1 message driven bean instantie aanroepen met het ontvangen bericht als argument.
21
Ordina J-Technologies
Cookbook EJB 3.0
NOTE: In de specificatie is gedefinieerd dat mappedName een product-specifieke naam is en dat applicaties die gebruik maken van mapped names niet portable kunnen zijn. Een portable manier is om gebruik te maken van de activationConfig attribuut (zie [EJB3S] voor de definitie). Een message driven bean kan zowel transactioneel als niet-transactioneel worden uitgevoerd. Het transactie-attribuut moet dan respectievelijk REQUIRED of NOT_SUPPORTED zijn. Op het moment dat een transactionele verwerking van een bericht faalt met een systeem exceptie, volgt er een rollback en zal het bericht terug op de queue worden geplaatst. Meestal kun je op de applicatieserver instellen na hoeveel pogingen een bericht naar de deadletter queue moet worden verplaatst, zodat het niet meer door een MDB wordt opgepakt. Het verzenden van een bericht is nog steeds relatief complex, maar wordt door het gebruik van dependency injection een stuk versimpeld:
In eerste instantie moet een verbinding op worden gezet met het onderliggende messaging systeem. Vervolgens wordt een sessie aangemaakt en een bericht. Als laatste wordt het bericht naar een bestemming (een queue) gestuurd. Afsluitend wordt de gebruikte resources weer afgesloten (normaal gesproken in een finally-blok!!). Van belang om je te realiseren is dat pas bij een commit van een transactie het bericht daadwerkelijk verstuurd wordt en niet bij de aanroep van de send-methode. Vind in plaats van een commit een rollback plaats, dan is het bericht dus niet verstuurd!
22
Ordina J-Technologies
Cookbook EJB 3.0
3.8. Stateful Session EJB Een Stateful EJB is bedoeld voor het bijhouden van de state ten behoeve van 1 client. Het is de vastlegging van een conversatie (conversational state) tussen client en server. Net als bij een stateless session bean is de interface een gewone java-interface.
In de implementatie is echter wel een onderlinge afhankelijkheid te bespeuren die de voortgang van de conversatie laat zien:
Hier zijn verschillende annotaties gebruikt. @PostConstruct is een zogenaamde callback methode en wordt aangeroepen voor de eerste aanroep van een business method door een client. In dit voorbeeld wordt hier een lijst van inschrijvingen geïnitialiseerd. Vervolgens kunnen de business methoden setStudent en add aangeroepen worden. De setStudent is een methode die zorg draagt voor een deel van de initialisatie. In eerdere specificaties zou dit via een create(Student) methode zijn geïmplementeerd. Met EJB 3.0 zijn dit soort initialisatie-methodes gewone business methods geworden.
23
Ordina J-Technologies
Cookbook EJB 3.0
Gedurende de conversatie van de cliënt met de EJB kunnen meerdere inschrijvingen worden toegevoegd die aan het einde van de conversatie geregistreerd/doorgevoerd worden in de database. De annotatie @Remove geeft aan dat na het succesvol afronden van de methode de container de conversatie zal beëindigen door de stateful EJB instantie te verwijderen. Toch aanroepen van een verwijderderde instantie levert een NoSuchObjectLocalException op (of Remote).
3.8.1. Verschil tussen 2.1 en 3.0 Er is een aanzienlijk verschil tussen stateful EJB’s in versie 2.1 en versie 3.0 van de specificatie. Het belangrijkste verschil is dat de expliciete creatie van een stateful EJB niet meer nodig is. Bij de lookup of bij dependency injection wordt de stateful session EJB geïnitialiseerd. Hiermee zijn dus aparte initialisatie methodes tot het verleden gaan behoren: initialisatie met business data zijn nu gewone business methods geworden.
24
Ordina J-Technologies
Cookbook EJB 3.0
3.9. Overig 3.9.1. Callbacks Callbacks zijn methoden die aangeroepen worden door de container bij het optreden van bepaalde events in de lifecycle van een EJB. De volgende events en bijbehorende annotaties zijn gedefinieerd: • • • •
PostConstruct PrePassivate (alleen stateful session EJB) PostActivate (alleen stateful session EJB) PreDestroy
PostConstruct wordt gegarandeerd aangeroepen voor de aanroep van een business methode. Dependency injection van alle resources heeft op dat moment plaatsgevonden. PreDestroy gebeurt als de container het component opruimt. Bij systeem excepties is de container mag de container de specifieke EJB-instantie direct weggooien en hoeft de methode die geannoteerd is met PreDestroy niet aangeroepen te worden. Stateful session EJB’s houden conversational state bij ten behoeve van een client. Voor efficiënt beheer van de resources van de applicatieserver, kan het nodig zijn dat de applicatieserver de state van stateful session EJB’s overzet van geheugen naar andere opslag. Dit proces wordt passivering genoemd. Het terugzetten van de state heet activering. Uiteraard is dit alleen mogelijk voor stateful session EJB-instanties die niet in gebruik zijn (idle). Binnen de specificatie is deze mogelijkheid onderkend en hier zijn de callback-annotaties PrePassivate en PostActivate voor gedefinieerd. PrePassivate wordt aangeroepen net voor het opslaan van de state van de EJB en PostActivate gegarandeerd voor het aanroepen van een business methode. Een methode die geannoteerd is met 1 of meerdere event typen wordt bij het optreden van dat event uitgevoerd.
25
Ordina J-Technologies
Cookbook EJB 3.0
3.9.2. Excepties Het exceptie-mechanisme is aanzienlijk veranderd in deze nieuwe specificatie. Gebleven is het onderscheid tussen applicatie- en systeem excepties. Applicatie excepties zijn onderdeel van de business logica. Deze worden dus gebruikt voor onvolledige invoer, overtreding van business rules en dergelijke aan te geven. Resultaat is dat deze excepties niet gewrapped worden door de container, maar ongewijzigd worden doorgegeven aan de cliënt van het component. De cliënt krijgt dus exact dezelfde exceptie als die door het EJB-component gegooid is. Nieuw is dat applicatie excepties checked of unchecked mogen zijn. Het onderscheid tussen een unchecked applicatie exceptie en een systeem exceptie is de annotatie @ ApplicationException. Daarnaast kent deze annotatie nog een interessant attribuut: rollback. Default wordt bij een applicatie exceptie geen rollback gedaan van de transactie. Door middel van rollback=true kan dit wel geregeld worden middels annotaties.
Systeem excepties zijn RemoteException of subklassen daarvan en alle niet met @ ApplicationException geannoteerde subklassen van RuntimeException. Op het moment dat een systeem exceptie wordt afgevangen door de container, wordt automatisch een rollback gedaan van de transactie (indien aanwezig) en wordt de instantie door de container opgeruimd. Hierbij is niet gegarandeerd dat de PreDestroy-callback wordt aangeroepen door de container. NOTE: De exceptie die geannoteerd is met @ApplicationException moet in de jar-file van de ejb aanwezig zijn, anders wordt de annotatie niet verwerkt. Het ‘oormerken’ van een runtime exceptie als applicatie exceptie die zich in een ander jar-file bevind, moet dan in de deployment descriptor gebeuren door middel van de
entry: <exception-class>nl.ordina.cookbook.FullException true
26
Ordina J-Technologies
Cookbook EJB 3.0
4. Persistentie met de Java Persistence API 4.1. Inleiding Bij EJB 3.0 is een volledig nieuwe aanpak voor persistentie toegevoegd aan de Java Enterprise Edition. Voor EJB 3.0 waren er alleen Entity Beans. Een Entity Bean is een component dat een objectgeoriënteerde view geeft op persistent opgeslagen entiteiten. Dit concept is nooit een succes geworden wat onder andere kwam door problemen op het gebied van complexiteit, performance en slechte fit met Object-Oriëntatie. De opkomst van Hibernate en Toplink, twee frameworks die uitgaan van object-relationele mapping door middel van POJO’s (Plain Old Java Objects) in plaats van componenten, maakte duidelijk dat het Entity Bean model ernstige tekortkomingen had. In de nieuwe specificatie zijn die lessen geïncorporeerd en is een programmeer model ontwikkeld dat uitgaat van gewone java-klassen in combinatie met mapping-informatie vastgelegd door middel van annotaties bij de java-klassen of in xml. Aangezien de specificatie een API beschrijft kunnen er meerdere implementaties zijn. Binnen de specificatie wordt zo’n implementatie een “Peristence provider” genoemd. De specificatie is zeer uitgebreid en zal in de meeste gevallen voldoen als mapping naar de database. Gezien de grootte van de specificatie is het niet mogelijk om alle verschillende onderwerpen aan de orde te laten komen. Zo zal het modelleren en implementeren van overerving, wat heel goed mogelijk is middels JPA, niet in dit cookbook behandeld worden. Daarnaast zal de JPQL (Java Persistence Query Language) beperkt behandeld wordt. Voor verdere informatie wordt verwezen naar de specificatie zelf [JPA] en het gratis te downloaden boek “Mastering EJB 3.0 [SBS].
27
Ordina J-Technologies
Cookbook EJB 3.0
4.2. Eerste persistente entiteit Het opzetten van de ontwikkelomgeving en inrichting van het project om het geschikt te maken voor persistentie is in de bijlagen beschreven.
4.2.1. Entiteitklasse De eerste en meest eenvoudige entiteit ziet er als volgt uit:
De annotatie entity geeft aan dat de klasse een persistent object is. Naast deze annotatie is er nog 1 annotatie verplicht om het een echte JPA-entity te laten zijn: een id annotatie. In dit geval is gekozen voor de eenvoudigste, de enkelvoudige uniek identificerend gegeven. Samengestelde id’s zijn ook mogelijk, maar zullen niet in dit cookbook worden behandeld. Aangezien deze klasse geen constructor heeft, wordt door de compiler automatisch een default public no-arg constructor gegenereerd. Een portable JPA applicatie vereist minimaal een no-arg constructor die public of protected is. Alle overige attributen zijn door de annotatie entity automatisch persistent. Uitsluiten van attributen van persistentie kan door middel van de annotatie @transient. Een persistence provider kan op twee manieren de persistente eigenschappen van een entiteit opvragen (bij mapping van database naar object) of zetten (bij opslag). Dit kan door direct de instantievariabelen van een object te gebruiken of door middel van het aanroepen van getters en setters. Dit wordt respectievelijk field of property access type genoemd. In bovenstaande voorbeeld maakt de annotatie bij het attribuut (@Id Long id) duidelijk dat hier is gekozen voor field access. Voor property access moeten de get-methoden worden geannoteerd. Aangezien het attribuut id en name niet van aanvullende annotaties voorzien zijn, vindt de mapping plaats via default mapping regels (zie [JPA] voor details). Een volledig geannoteerd voorbeeld ziet er als volgt uit:
28
Ordina J-Technologies
Cookbook EJB 3.0
4.2.2. Aanmaken Persistence Unit Entiteiten maken altijd deel uit van een persistence unit. Een persistence unit is een set van entiteiten en gerelateerde klassen die dezelfde configuratie delen. Denk hierbij aan onder andere dezelfde datasource. Deze configuratie is vastgelegd in een xml-configuratiebestand (persistence.xml). Een persistence unit is ook een grens. Het is een grens die afhankelijkheden tussen persistente klassen niet kunnen overschrijden. Queries over objecten hebben maximaal de reikwijdte van de persistence unit en niet verder. In de meeste applicaties zal gewoon sprake zijn van één persistence unit. De persistence unit voor JPA binnen de container ziet er als volgt uit:
Elke persistence-unit heeft een naam. Deze naam moet onderscheidend zijn voor alle persistence-units die zich binnen dezelfde scope bevinden (ejb-jar, jar of war-file). “Transaction-type” wordt gebruikt om aan te geven of de entity managers JTA transacties (Java Transaction API) gebruiken of dat de transactie-demarcatie wordt gedaan middels entitymanager (application managed). In een container-omgeving is de default JTA. In ons voorbeeld had dit attribuut dus achterwege gelaten kunnen worden. Bij application-managed transactie-demarcatie is het “transaction-type” RESOURCE_LOCAL. De application-managed transactie-demarcatie gaat door middel van entitymanager. getTransaction().begin() en commit().Expliciet transactie demarcatie zal praktisch gezien binnen EJB’s alleen gebruikt worden bij afwezigheid van een JTA-transactie. Voor deployment van de persistence-unit in een Java EE container is alleen de datasource nog van belang om op te geven. In dit voorbeeld wordt gebruik gemaakt van de standaard jta datasource van Glassfish met de JNDI-naam “jdbc/__default”. Het properties gedeelte is voor leverancierspecifieke properties. In dit geval alleen het soort database wat gebruikt wordt.
29
Ordina J-Technologies
Cookbook EJB 3.0
4.2.3. EJB met JPA Het eerdere voorbeeld met EJB wordt nu verder uitgebreid door toevoeging van de persistentie van objecten.
Defaults
Het is goed om stil te staan wat hier achter de schermen en door middel van convention over configuration gebeurt. Het default transactie mechanisme voor EJB’s is container managed transactions met een default transactie-attribuut REQUIRED. Aangezien bij de @PersistenceContext niet is aangegeven welk type het is, is het default transaction scoped. Dit betekent dat de entity manager die verkregen is door middel van dependency injection binnen de scope van de gestarte container transactie opereert. Dit geldt voor alle EJB’s die in de context van dezelfde transactie worden aangeroepen. Als de transactie eindigt, eindigt ook de persistence-context. Als laatste geldt dat er geen naam is meegegeven aan de persistence context. Default wordt dan de enig aanwezige persistence unit gekozen (als er meerdere persistence units voorkomen volgt een foutmelding!). JPQL
Vervolgens is de implementatie van de methode voor het opvragen van alle cursussen gewijzigd. In dit voorbeeld is gebruik gemaakt van de Java Persistence Query Language (JPQL) voor de query om alle cursussen op te halen. Deze taal maakt het mogelijk queries te definiëren op het niveau van het objectmodel. De persistence provider is verantwoordelijk voor de vertaling naar het SQL-statement dat geschikt is voor het database-platform dat gebruikt wordt. Alternatief voor het formuleren van een query d.m.v. JPQL, is het maken van native query in SQL. Op performance-gronden kan dit soms nodig zijn, omdat op deze manier specifieke functionaliteit van het specifieke databaseplatform gebruikt kan worden. Door gebruik van native SQL komt de portabiliteit van de applicatie echter in gevaar en wordt een groot gedeelte van de kracht van de automatische mapping van tabellen naar objecten niet gebruikt.
30
Ordina J-Technologies
Cookbook EJB 3.0
De add-methode geeft een voorbeeld van het opslaan van een nieuw object in de database. Na het aanroepen van de persist-methode wordt newCourse een door de persistence context gemanaged object. De specificatie geeft niet aan of op dit moment al een statement uitgevoerd moet worden op de database (flushen). Voor het moment van committen van de container managed transaction moet het nieuwe object geflushed worden richting de database. Vervolgens volgt de daadwerkelijke commit van de database transactie.
4.3. JPA binnen Java SE In de terugkoppeling op de eerste draft-specificaties was het meest gehoorde verzoek dat de persistentie specificatie uitgebreid werd, zodat deze geschikt zou zijn voor gebruik buiten een container. Hier is gehoor aan gegeven. In deze paragraaf zal voor het testen een voorzet worden gegeven voor het gebruik van de Java Persistence API in unittesten. Voor het buiten de container testen in een speciale opzet nodig voor de persistence. xml. Omdat nu geen gebruik gemaakt kan worden van een in de container gedefinieerde datasource, moet de datasource bij de persistence unit gedefinieerd worden:
Het transactie-type voor buiten de container is altijd RESOURCE_LOCAL wat aangeeft dat de transactie zelf expliciet gemanaged gaat worden. Aanvullende eis voor gebruik binnen de standaard Java runtime is een opsomming van alle persistente klassen. Dit is de enige manier die portable is tussen verschillende Persistence Providers. Het properties-gedeelte is bedoeld voor specifieke properties van de leverancier van de JPAimplementatie. In ons voorbeeld is dit de JDBC-url voor de database, JDBC-driver en het feit dat alle tabellen gedropped en opnieuw gecreëerd moeten worden. In dit voorbeeld wordt gebruik gemaakt van Derby, een open source database. De create=true in de JDBC-url zorgt ervoor dat in de workspace de database wordt gecreëerd indien deze niet aanwezig is. De drop-and-create-tables betekent dat, elke keer als de JVM gestart wordt, dat alletabellen gedropt worden en vervolgens weer gecreëerd. De uitgangssituatie is dus elke keer hetzelfde.
31
Ordina J-Technologies
Cookbook EJB 3.0
Hier volgt een voorbeeld van een eenvoudige JUnit-test voor het opslaan van een cursus.
Persistence is een bootstrap-klasse die alleen nodig als JPA buiten de container gebruikt wordt. Hieraan wordt een EntityManagerFactory met de naam van de persistence unit gevraagd. Vervolgens wordt een EntityManager aangemaakt. De EntityManager is niet thread-safe en managed de persistente instanties. Vervolgens wordt in de test een transactie gestart en een object opgeslagen middels de persist-operatie, waarna een commit volgt. Om dit voorbeeld werkend te krijgen, dienen de volgende libraries worden toegevoegd: • derby Lokale in-memory database (GLASSFISH_HOME\javadb\lib). • toplink-essentials Referentie implementatie van JPA (GLASSFISH_HOME\lib).
32
Ordina J-Technologies
Cookbook EJB 3.0
4.4. Associaties Een domeinmodel kan niet zonder associaties tussen objecten. Een definitie van een cursus is leuk, maar zonder dat deze cursus daadwerkelijk wordt gegeven, is deze nog van weinig waarde. Zie hier de introductie van de klasse Cursusaanbieding inclusief de associatie met cursus.
Een cursus is alleen de definitie. Een cursus aanbieding (offering) is de daadwerkelijke uitvoering van een cursus. Een cursus kan dan ook meerdere keren aangeboden (gegeven) worden. In eerste instantie wordt in het model alleen het opvragen van de cursus informatie vanuit een cursusaanbieding ondersteund. Dit wordt als volgt geïmplementeerd.
De klasse CourseOffering heeft een attribuut dat referereert aan een cursus. De annotatie @ManyToOne is nodig om aan te geven dat het een associatie betreft, waarbij meerdere aanbiedingen bij een cursus kunnen horen. In de cursuscase is het ook van belang dat vanuit een cursus opgevraagd kan worden welke cursusaanbiedingen gedaan worden en dat er nieuwe aanbiedingen van de cursus toegevoegd kunnen worden. Het voorbeeld wordt uitgebreid zodat de navigatie bidirectioneel wordt. De Course-klasse wordt als volgt gewijzigd:
33
Ordina J-Technologies
Cookbook EJB 3.0
Het mappedBy-attribuut is noodzakelijk bij bidirectionele associaties tussen objecten. Het mapped-By attribuut moet gelijk zijn aan de naam van de instantievariabele van het andere object in de associatie die refereert naar dit object (this). In het voorbeeld heeft de klasse CourseOffering een instantievariabele course die verwijst naar de Course-klasse. De Courseklasse heeft het volgende mapped-by attribuut: mappedBy=”course”. NOTE: De ontwikkelaar is verantwoordelijk is voor het consistent houden van beide kanten van een bidirectionele relatie (zie ook [HORST]). Alle Offerings die voorkomen in de lijst moeten dus een verwijzing hebben naar hetzelfde Course-object. Afwijkingen leveren onvoorspelbaar gedrag op. Vandaar de toevoeging van een nieuwe CourseOffering met in de constructor de cursus, waartoe de aanbieding behoort, als parameter. Belangrijk voor OneToMany en ManyToMany associaties is dat default de persistence provider gebruik mag maken van lazy fetching. Lazy fetching in ons voorbeeld houdt in dat de aanbiedingen die horen bij een cursus pas worden opgehaald op het moment dat dat nodig is, bijvoorbeeld als het aantal aanbiedingen wordt opgevraagd van een cursus. Voor alle basis typen (Integer, String, e.d.), OneToOne en ManyToOne associaties is de default eager fetching. Dit betekent dat op het moment dat een object wordt opgehaald uit de database deze attributen ook direct worden opgehaald. Dit default gedrag kan veranderd worden door het fetch attribute op te geven.
Nu moet de persistence provider alle cursusaanbiedingen ophalen op het moment dat de cursus opgehaald is uit de database. NOTE: EAGER is een verplichting voor de persistence provider, LAZY is slechts een hint. De persistence provider mag er voor kiezen om toch EAGER te fetchen.
34
Ordina J-Technologies
Cookbook EJB 3.0
4.5. Levenscyclus van een entiteit De levenscyclus van een entiteit is van groot belang. Afhankelijk hiervan is het aanroepen van methoden op de entity manager interface wel of niet toegestaan.
Refresh is een methode die alleen gebruikt kan worden bij gemanagede objecten. Refresh zorgt er voor dat de laatste stand uit de database wordt opgehaald en het object ‘ververst’ wordt.
4.5.1. Nieuw object Een object begint zijn leven bij de aanroep van de constructor en heeft dan de status New. Op het moment dat de persist-operatie van de EntityManager wordt aangeroepen wordt het object beheerd door de persistence provider. De status is gewijzigd naar Managed Volgt een rollback van de transactie binnen dezelfde transactie als waarin de persist is aangeroepen, dan krijgt het object weer de status new/transient. Het persisteren van het object is immers door de rollback teniet gedaan. De rollback die leidt tot de overgang van Managed naar Detached geldt alleen voor objecten die voor het begin van de transactie al gepersisteerd waren (zie 4.4.2 Object uit database). Volgt echter een commit dan is het object daadwerkelijk opgeslagen in de database. Omdat de transactie gesloten wordt, eindigt de persistence context en wordt het object niet meer beheerd door de persistence provider. Het is detached (unmanaged) geworden.
35
Ordina J-Technologies
Cookbook EJB 3.0
4.5.2. Object uit database Op het moment dat een object uit de database wordt opgehaald door bijvoorbeeld een query, krijgt het de status managed. Als vervolgens een commit of rollback van de transactie plaats vindt of een close() van de EntityManager, dan wordt de status van het object detached. De persistence context eindigt immers. Als op de entitymanager remove wordt aangeroepen krijgt het object de status removed. Een rollback zorgt er voor dat de verwijdering ongedaan wordt gemaakt en dat het object de status detached wordt. De commit zorgt ervoor dat het object daadwerkelijk uit de database is verwijderd. Het java-object is dan geen persistent object meer en heeft dan de status transient.
4.5.3. Detached objects and merging Een persistence context kan expliciet gesloten worden middels het aanroepen van de close-operatie op de EntityManager interface of impliciet via een commit/rollback van een container transactie in een gemanagede omgeving. Dit leidt er toe dat alle gemanagede object detached worden. Ze zijn losgekoppelde van hun persistence context. Ze worden niet meer gemanaged. De koppeling tussen een detached object en een persistence context kan hersteld worden door gebruik te maken van de merge operatie. Dit betekent dat het object weer gemanaged worden door een persistence context en dat eventuele wijzigingen die op het detached object zijn aangebracht bij een commit worden opgeslagen in de database. Een veelvoorkomend scenario in een webapplicatie is dat een object vanuit de businesslag (een EJB) wordt opgehaald uit de database en door de presentatielaag gepresenteerd wordt aan de gebruiker. De gebruiker vult in een formulier een aantal wijzigingen in en vervolgens worden deze wijzigingen aan de businesslaag aangeboden. Op het moment dat de objecten zijn opgehaald en teruggegeven aan de presentatielaag zijn ze detached geworden. Op het moment dat een EJB dit object inclusief wijzigingen weer terugkrijgt dient het eerst gemerged te worden, zodat het object weer gemanaged wordt door de persistence context.
36
Ordina J-Technologies
Cookbook EJB 3.0
Merge is een vreemde eend in de bijt. De meeste operaties van de entity manager interface werken op gemanagede objecten. Merge daarentegen verwacht een een detached object en geeft een managed object terug. Het correcte gebruik is dus:
pCourse is dus een detached object, wat verder niet meer gebruikt dient te worden. De variabele course verwijst dus naar het gemanagede object, dat wordt teruggegeven door de EntityManager.
4.6. Transacties Voor gebruik van de EntityManager van JPA binnen EJB 3.0 is een transactie nodig (transactie attribuut Required, RequiresNew en Mandatory). Veel methoden van de EntityManager interface geven een TransactionRequiredException als ze worden aangeroepen zonder een transactie. Enige uitzondering op deze regel is bij stateful EJB in combinatie met een extended persistence context. De extended persistence context zal niet behandeld worden in dit cookbook.
37
Ordina J-Technologies
Cookbook EJB 3.0
4.7. Optimistic locking Optimistic locking is een techniek die er voor zorgt dat updates op een bepaalde entiteit alleen worden doorgevoerd zolang er geen transactie is geweest die de data heeft gewijzigd sinds het moment van lezen van de data. Een gebruikelijk scenario is dat een entiteit wordt opgehaald uit de database en wordt getoond aan de gebruiker. De gebruiker maakt aanpassing en wil de aanpassing persistent maken. Als in de periode tussen ophalen en opslaan een andere gebruiker dezelfde entiteit wijzigt, zal tijdens het mergen van de state van de entiteit een OptimisticLockException worden gegooid om aan te geven dat er tussentijdes wijzigingen zijn geweest. Optimistic locking wordt geïmplementeerd door bij elk object een versie vast te leggen. De versie wordt in de regel vastgelegd door middel van een volgnummer of de datum/tijd van de laatste wijziging. Bij het opslaan van een wijziging wordt de versie van het gewijzigde object vergeleken met de versie van het opgeslagen object. De @Version annotatie geeft aan dat een attribuut van een entiteit dient voor optimistic locking. De versie wordt gebruikt om de integriteit te waarborgen tijdens de merge operatie en voor optimistic concurrency control. Door toevoeging van onderstaande code wordt optimistic locking automatisch enabled op een entiteit:
Voor portable applicatie mag slechts 1 attribuut per entiteit worden aangemerkt als versie en deze moet tevens een numeriek of een timestamp veld zijn. Van belang om je te realiseren is dat in de meeste gevallen de applicatie het versienummer nooit zelf moet wijzigen. Het wijzigen van het versienummer is de verantwoordelijkheid van het onderliggende persistentie-framework en niet van de applicatie. Batch-updates, waarbij een applicatie in één keer een groot aantal objecten wijzigt, is één van de weinige voorbeelden waar de applicatie zelf het versienummer kan wijzigen.
38
Ordina J-Technologies
Cookbook EJB 3.0
4.8. Java Persistence Query Language De JPQL (Java Persistence Query Language) wordt gebruikt voor het definieren van queries over entiteiten en hun persistence state. Het is een taal die portable is, dit wil zeggen onafhankelijk van de database in een bepaalde omgeving. JPQL is een extensie van EJB QL (EJB Query Language) die in gebruik was bij de snel te vergeten Entity Beans. Door het forse aantal uitbreidingen is JPQL een taal die in de meest voorkomende gevallen voldoende is voor applicaties, zodat niet hoeft te worden uitgeweken naar leveranciers specifieke extensies (zoals Hibernate Query Language) of naar native SQL. In beide gevallen gaat dit ten kosten van de portabiliteit van de applicatie. Het gaat te ver om alle onderdelen van de JPQL in dit cookbook te behandelen. Hiervoor zijn [JPA] en [KODO] goede reference guides. Wel zullen in de komende paragrafen een korte introductie en een aantal tips gegeven worden voor het omgaan met JPQL. De entitymanager is een factory het aanmaken van queries. Voor de methode createQuery moet een JPQL-query meegegeven worden:
De andere mogelijkheid voor het specificeren van een JPQL-query is een named query. Dit is een query die als annotatie op de entiteit-klasse vastgelegd kan worden.
Vervolgens kan deze query als volgt binnen code gebruikt worden:
39
Ordina J-Technologies
Cookbook EJB 3.0
4.9. Mapping In de volgende paragrafen zullen de mogelijkheden behandeld worden die JPA biedt voor mapping van objectattributen op databasevelden.
4.9.1. Embeddable objects De structuur van een database is vaak niet 1-op-1 over te zetten naar de wereld van ObjectOriëntatie. Vaak zal een klasse samengesteld zijn uit andere klasses die meer fine-grained zijn, terwijl uiteindelijk alles wordt opgeslagen in 1 tabel. Denk hierbij aan een persoon-tabel waarin straat, huisnummer etc is opgenomen.
Binnen Java zouden we dit als volgt kunnen modelleren, waarbij generiek gedrag/validaties rond adressen in de adres-klasse geplaatst kan worden:
en
Door default mapping regels zullen alle attributen van adres een corresponderende kolom krijgen in de persoon-tabel. Vooral bij de mapping van bestaande databases zal het niet het geval zijn dat als persoon en klant beide een adres hebben, dat de kolommen in de respectievelijke tabellen exact dezelfde naam hebben. Door middel van annotaties is het mogelijk om voor zowel persoon- als de klant tabel een eigen mapping te maken voor het object adres:
40
Ordina J-Technologies
Cookbook EJB 3.0
4.9.2. Enumeraties Voor enumeraties bevat de JPA ook ondersteuning. Enums kunnen worden opgeslagen als een String of als een integer (STRING of ORDINAL). Default worden enumeraties opgeslagen als ordinal. Voorbeeld: Voor het type contract is een enumeratie gedefineerd, die bestaat uit twee waarden: full time en parttime.
Mapping van de enumeratie in een persistente klasse ziet er als volgt uit:
Hierbij zal de kolom contractType de waarde “FULL_TIME” of “PART_TIME” bevatten. De inhoud van de kolom is dus gelijk aan de naam van de constante! Bij het gebruik van EnumType=Ordinal wordt FULL_TIME 0, en PART_TIME 1, etc. De waarde wordt dus bepaald door de volgorde in de enumeratie! Vooral bij herschikkingen van enumeraties kan dit onverwachte bijwerkingen hebben.
4.9.3. Mapping files Alle object-relationele mapping informatie die door middel van annotaties kan worden uitgedrukt, kan ook door middel van een mapping file in xml formaat worden aangegeven. Dit maakt het mogelijk om domeinobjecten zonder enige JPA-annotatie te maken en alle mapping informatie vast te leggen in aparte xml-bestanden. Een (deels gevulde) mapping file ziet er als volgt uit:
41
Ordina J-Technologies
Cookbook EJB 3.0
4.10. Wat zit niet in de Java Persistence API? De JPA heeft heel veel functionaliteit, maar toch worden een aantal zaken die niet door de specificatie wordt gedekt. Dit zijn onder andere: • • •
second level cache een Criteria API pessimistic locking
4.10.1. Caching Persistence Context is vergelijkbaar met de first-level cache. Over de second-level cache is niets gespecificeerd in de JPA specificatie. Hier wordt dus ruimte gelaten voor caching oplossingen van persistence providers zoals Toplink, Hibernate en Kodo.
4.10.2. Criteria De Criteria API van Hibernate is een eenvoudige manier om via Java-methodes een query op te bouwen. Dit ontbreekt in de JPA. Er wordt alleen de mogelijkheid geboden om door middel van de Java Persistence Query Language objecten op te halen. Criteria is buiten de 3.0-specificatie gelaten, omdat het als te exotisch werd beschouwd. Op het moment van schrijven zijn er plannen om dit in het Spring framework op te nemen in versie 2.1. Daarnaast staat het al wel op de wensenlijst voor JPA 1.1 versie.
4.10.3. Pessimistic Locking Alleen optimistic locking wordt door de specificatie voorgeschreven. Pessimistic locking wordt niet gedekt door de specificatie en wordt dus overgelaten aan de persistentie providers.
42
Ordina J-Technologies
Cookbook EJB 3.0
4.11. JPA en Spring Een onderdeel van Spring 2 is Spring JPA. Spring JPA geeft op dezelfde manier integratie voor JPA als op dit moment ook voor hibernate of JDO aanwezig is. Spring biedt hiervoor de JpaTemplate voor gebruik in een data access object en de JpaDaoSupport als klasse waarvan een dao kan overerven. Als gekozen wordt voor JPA als de persistentie oplossing binnen de EJB layer, dan lijken deze klassen vrij weinig toe te voegen. Wat wel handig kan zijn, is als gekozen wordt voor het gebruik van JPA binnen een web applicatie. Spring biedt dan voordelen rond het creëren en sluiten van een entity manager. Door gebruik van Spring is het mogelijk om in een web applicatie JPA op dezelfde manier te gebruiken alsof je met EJB’s zou werken.
43
Ordina J-Technologies
Cookbook EJB 3.0
5. Interceptors Interceptors zijn een nieuwe feature binnen de EJB-specificatie en bieden een beperkte vorm van het Aspect Oriented Programming. Interceptors worden gebruikt voor het adresseren van cross cutting concerns bij het aanroepen van business methoden en voor events in de levenscyclus van een EJB. Het maakt een helder onderscheid tussen business logica in de EJB en meta- of support code. Interceptors maken het mogelijk om gedeeld gedrag dat op meerdere punten in de applicatie voorkomt te enkapsuleren.
5.1. Use cases Use cases voor interceptors zijn: •
• •
•
Method profiling Typische cross-cutting concern is profiling, waarbij informatie rond het tijdsgedrag van de applicatie in kaart kan worden gebracht. Auditing Custom security Op het moment dat eisen aan de autorisatie van EJB-methoden worden gesteld die de JEE-specificatie te boven gaan, kunnen interceptors een oplossing bieden. Framework extension Interceptors worden al in een aantal frameworks gebruikt die als uitbreiding dienen op de EJB specificatie. Denk hierbij aan Spring en Seam.
5.2. Interceptor voorbeeld Er zijn verschillende manieren om een interceptor aan een EJB-klasse of methode te koppelen. Voor herbruikbare interceptors is het het beste om ze in een aparte klasse op te nemen. Een voorbeeld implementatie van een profiling interceptor ziet er als volgt uit:
De annotatie @AroundInvoke geeft aan dat de omvattende klasse een interceptor is en dat methode profile de intercepting methode is. Slechts één methode binnen een klasse mag geannoteerd worden met @AroundInvoke.
44
Ordina J-Technologies
Cookbook EJB 3.0
De invocation context is een interface, die informatie geeft over het “onderschepte” object en de methode die aangeroepen wordt. Daarnaast is het mogelijk om parameters op te vragen en te wijzigen. Het aantal use cases voor deze laatste functionaliteit zal beperkt zijn. De methode proceed() op de invocation context moet één keer worden aangeroepen. Door deze aanroep zal de container de volgende interceptor in de interceptor keten aanroepen of de eigenlijke business methode uitvoeren. Dit maakt het ook mogelijk, bijvoorbeeld bij een interceptor voor custom security, om juist niet te delegeren als aan bepaalde voorwaarden is voldaan.
5.3. Koppelen van interceptor Gebruik van de interceptor kan door middel van een annotatie op de klasse of op de methode.
In dit voorbeeld wordt de interceptor gebruikt voor elke aanroep van een business methode op deze EJB. Gebruik van interceptors kan ook via de deployment descriptor. Als bepaalde interceptors voor alle EJB’s gebruikt moeten worden, kan dit alleen maar via de deployment descriptor. Daarnaast geeft de deployment descriptor de mogelijkheid om het gebruik van interceptors dat is aangegeven via annotaties te disablen.
De “*“ geeft aan dat de interceptor voor alle EJB’s geldt die binnen het subsysteem zitten.
45
Ordina J-Technologies
Cookbook EJB 3.0
5.4. Enterprise Naming Context Interceptors behoren tot dezelfde Enterprise Naming Context (ENC) als de EJB waarvoor de methoden onderscheppen. Net als de EJB kunnen ze gebruik maken van alle mogelijkheden van resource injection. In onderstaand voorbeeld staat een auditing interceptor die van verschillende onderdelen van de ENC gebruik maakt om informatie over methode aanroepen in een audit log, in dit geval in de database, weg te schrijven.
5.5. Levenscyclus Belangrijk om te onthouden bij het gebruik van interceptors, is dat: •
• •
46
de levenscyclus van een interceptor-instantie is verbonden met de 1 instantie van een EJB. De interceptor wordt dus op dezelfde moment gecreeerd, gepassiveerd, geactiveerd en verwijderd als de EJB waar hij bijhoort. kan ook gebruik maken van de dependency injection en JNDI-entries van de EJB waar hij bijhoort de interceptor heeft dezelfde security en transactie context als de business methode van de EJB. Als een ejb-methode wordt aangeroepen waarin een transactie nodig is, wordt de transactie gestart voordat de interceptor wordt aangeroepen. De interceptor kan dus ook zorgen voor een rollback van de transactie door het gooien van een systeem exception of het aanroepen van setRollbackOnly
Ordina J-Technologies
Cookbook EJB 3.0
5.6. Waarschuwing In de Glassfish implementatie is onderzoek gedaan naar het effect van meerdere interceptors op performance (zie [OAKS]). In onderstaande figuur is duidelijk te zien dat het toevoegen van 5 interceptors een aanzienlijke performance degradatie tot gevolg heeft. Kortom: verifieer dat bij het vormen van een keten van interceptors de performance acceptabel blijft!
47
Ordina J-Technologies
Cookbook EJB 3.0
6. Assembly 6.1. Deployment Descriptor Voorgaande edities van de specificatie waren berucht om de J2EE deployment descriptors. Naast de gespecificeerde descriptors waren er per applicatie server ook nog server specifieke deployment descriptors, waardoor het bewerken van de deployment descriptor eigenlijk alleen goed te doen was met tooling van de applicatieserver of aparte tooling van de leverancier van de applicatieserver. Daarnaast is het vervelend dat voor het uiteindelijke gedrag van de applicatie je twee plekken moet bekijken: de EJB klasse en zijn deployment descriptor. Dit heeft onder andere het pad geëffend voor frameworks als xdoclet. Annotaties zijn met deze specificatie geintroduceerd om de deployment descriptor voor eenvoudige applicaties zo goed als overbodig te maken. Daarnaast hoeven deployment descriptors niet meer volledig gespecificeerd te worden. Informatie in de deployment descriptor kan aanvullend zijn (nog niet gespecificeerd middels annotaties) of overschrijvend.
6.2. Packaging Het samenstellen van de enterprise application is veel eenvoudiger geworden met de Java Enterprise Edition 5. Een Java EE applicatie bestaat uit één of meerdere Java EE Componenten/Modules en een optionele deployment descriptor. Bij afwezigheid van een deployment descriptor worden de applicatiemodules ontdekt via standaard regels. De belangrijkste standaardregels zijn: •
•
bestanden met de extensie war worden beschouwd als webmodules, waarbij de context root van een web-applicatie gelijk is aan het pad van het bestand zonder de extensie bestanden met de extensie jar die niet in de lib-directory staan, waarin klassen aanwezig zijn met een EJB component annotatie (@Stateless etc.) worden beschouwd als EJB module
De lib directory binnen een ear-file is (default) een speciale directory geworden. Alle library jar-files die in deze directory staan zijn beschikbaar voor alle modules in de applicatie. Dit is te wijzigen door middel van het element in de deployment descriptor. Maak hier alleen gebruik van als het echt niet anders kan, omdat dit de duidelijkheid en transparantie niet ten goede komt.
48
Ordina J-Technologies
Cookbook EJB 3.0
Aangezien de lib directory standaard ter beschikking staat voor alle modules, is dit ook een handig plek om de interfaces van de EJB-componenten te plaatsen. NOTE: Plaats geannoteerde EJB componenten dus niet in de lib-directory aangezien deze dan niet automatische als EJB module worden beschouwd en dus niet door de container geladen worden. Dit resulteert in de volgende onderdelen van het Enterprise Archive met hun onderlinge afhankelijkheden:
CursusSysteemWeb is een webapplicatie die afhankelijk is van het domein model (de entiteiten) en van de interface van de EJB-componenten. CursusSysteemEJB bevat de daadwerkelijke implementatie van de services. Deze is afhankelijk van het domein model en uiteraard van de interfaces die het implementeert.
49
Ordina J-Technologies
Cookbook EJB 3.0
7. Best practices 7.1. Core EJB 7.1.1. Prefereer local interfaces boven remote interfaces Lokale interfaces hebben per definitie een betere performance dan remote interfaces. Een kopieerslag of serialisatie is bij lokale interfaces niet nodig, omdat hier gebruik wordt gemaakt van pass-by-reference in tegenstelling tot de pass-by-value bij remote interfaces.
7.1.2. Annotaties op EJB interfaces Er zijn twee mogelijkheden om aan te geven of een interface van een EJB lokaal of remote is. Dit kan op de interface zelf
of op de EJB klasse
Conceptueel is het annoteren van de EJB klasse beter. De interface is immers een technologieonafhankelijke declaratie van het contract. De EJB klasse is de platform-specifieke implementatie. Nadeel van annotatie op de EJB-klasse is dat aan de interface niet te zien is of deze al dan niet remote gebruikt kan worden.
7.1.3. Interceptor Het gebruiken van de EJB-klasse zelf voor het definiëren van een interceptor-methode is in zekere zin strijdig met de AOP-gedachte. De interceptor heeft een harde koppeling met het object waar hij de aanroepen van onderschept. Ditzelfde geld ook voor het annoteren van een EJB-klasse of –methode met de @Interceptorannotatie. Dit brengt een directe koppeling tot stand tussen het object en zijn interceptor. Voorkeur voor echt generieke interceptors kunnen het best door middel van de deployment descriptor tijdens assembly of deployment van de applicatie aan de EJB gekoppeld worden. Herbruikbare interceptors kunnen het beste als losse klassen gedefinieerd worden, zodat ze ook daadwerkelijk hergebruikt kunnen worden.
50
Ordina J-Technologies
Cookbook EJB 3.0
7.1.4. Deployment descriptors Met de komst van annotaties en dependency injection is ontwkkeling van JEE-applicaties een stuk eenvoudiger geworden. Toch is hiermee de rol van de deployment descriptor nog niet uitgespeeld. De annotaties die een semantisch onderdeel zijn van de applicatie zijn onder andere de afhankelijkheden op resources in de omgeving en het transactionele gedrag van componenten. Element zoals beveiliging en interceptors, als ook het eventueel overschrijven van mapping van resources kan het best door middel van de deployment descriptor.
7.2. JPA 7.2.1. Annotaties versus mapping file Mapping van entiteiten kan zowel door middel van annotaties op de klasse of door middel van een XML-mapping file gebeuren. Het is zelfs mogelijk om beide te doen. In dat geval is de definitie uit de mapping file leidend. Het is belangrijk om je te realiseren dat de mapping file niet aanvullend is ten opzichte van de annotaties, maar overschrijvend. Als bij een attribuut de annotaties @Temporal en @Column staan, wordt door het in de mapping file opnemen van de kolom-specificiatie de @Temporal annotatie genegeerd. In de mapping file zal dus ook het TemporalType moeten worden overgenomen. De keuze tussen annotaties of mapping file zal per project moeten worden gemaakt. Bij de beslissing is onder andere van belang wat het type project het is. Voor een Proof-of-concept is het verstandig om in verband met snelheid te werken met annotaties. Bij het volledig uitspecificeren van de mapping en het genereren van de database is het verstandig in verband met de leesbaarheid de mapping in een aparte file te plaatsen. Als laatste overweging moet naar het gebruik van named queries kijken en bij wie de verantwoordelijkheid voor de mapping is gelegd. Als binnen het project de beslissing wordt genomen om alle of de meeste queries als named query op te nemen, dan is de deployment descriptor het beste alternatief. Het is makkelijker voor eventuele betrokken DBA’s om de verschilende JPA-queries te bekijken om bijvoorbeeld het databasemodel te tunen. Een andere overweging is wie verantwoordelijk is voor een mapping. Is deze apart belegd bij een JPA / database expert, dan is het gebruik van een mapping file logisch. Op die manier zitten personen verantwoordelijk voor de ontwikkeling van business logica en de mapping van de objecten naar de database elkaar niet in de weg.
51
Ordina J-Technologies
Cookbook EJB 3.0
7.2.2. EJB 2.1 style Entity Beans Voor EJB 3.0-compliancy is backwards compatibiliteit met EJB 2.1 vereist. Dit houdt in dat ook de EJB 2.1 Entity Beans nog ondersteund worden. Ontwikkel geen nieuwe entity beans meer ! Gebruik de Java Persistence API.
7.2.3. Packaging entities Zorg ervoor dat entiteiten die behoren bij 1 persistence unit standaard bij elkaar gepackaged worden in één jar-file. Een persistence-unit is immers een logische eenheid van gemanagede persistente klassen, de configuratie-informatie en hun mapping metadata.
7.2.4. Primitive type of Wrapper Bijvoorbeeld het gebruik van int of Integer. Gebruik de wrapper klassen. Hierin kan aangegeven worden dat een attribuut nog geen waarde heeft gekregen. Bij primitieven is er altijd een default waarde (0), waardoor dit onderscheid niet meer gemaakt kan worden. Gebruik standaard Wrapper klassen vanwege consistentie en om het onderscheid te kunnen maken tussen 0 en null waarden. Zelfs als het attribuut not-null is, heeft dit toegevoegde waarde. Instanties, waarvan het id null is, zijn nieuw en nog niet opgeslagen in de database.
7.2.5. Naamgeving Named queries Named queries hebben geen scope. Een named query met de naam “findAll” die gedefinieerd is op klasse X conflicteert met de query “findAll” op klasse Y. Gebruik daarom een naamgevingsconventie die ook de scope aan geeft: bijvoorbeeld de entiteit die in het resultaat is van de query. Een duidelijke conventie is find<Entiteit>By. Voorbeeld. findTeacherByFirstName/zoekDocentMetVoornaam.
52
Ordina J-Technologies
Cookbook EJB 3.0
7.2.6. Named parameters Voor queries kan gebruik worden gemaakt van named of positional parameters: select d from Docent d where d.voornaam = :naam select d from Docent d where d.voornaam = ?1 Positional parameters beginnen bij 1 en mogen geen gaten in de reeks hebben. Dit is minder duidelijk en expressief dan de named parameters. Gebruik daarom standaard named parameters.
7.2.7. Constructor Toplink geeft geen foutmelding als de constructor van een persistente klasse private is. Hibernate wel (java.lang.IllegalArgumentException: No visible constructors in class). De specificatie schrijft voor dat er minimaal een public of protected default constructor op een persistente klasse aanwezig moet zijn. Voeg voor portabiliteit deze altijd toe.
7.2.8. Field versus property accesstype De Persistence Provider kan op twee manieren data manipuleren van persistente objecten. Eén is door direct de velden van het persistence object te manipuleren (field). De ander door JavaBean property style te gebruiken en de get/is/set-methode van de objecten aan te roepen. Binnen een entiteit zorgt onderstaande statement met annotaties voor accesstype field: @Column private String naam; Voor accesstype property moet de annotatie op de getter worden geplaatst. Aanvullend geldt hier dat de getter en setter public of protected visibility moet hebben. Dit sluit package private (de default access) and private uit! Let op: Vermijd business logica in setters in combinatie met accesstype property. De business
logica wordt namelijk ook uitgevoerd bij het vullen van het object door de persistence provider. De volgorde waarin setters van het persistente object worden aangeroepen door de persistence runtime is niet gegarandeerd. Gecombineerde controles kunnen dus hiermee opgelost worden. Daarnaast is het meestal niet gewenst dat bij het ophalen van object uit de database de controles wederom worden uitgevoerd
53
Ordina J-Technologies
Cookbook EJB 3.0
Voor portabiliteit geldt dat er maar één accesstype gedefinieerd mag zijn in een entitieithiërarchie. Om conflicten in een hiërarchie van entiteiten te voorkomen, is het het beste om bij het begin van het project te bepalen welk accesstype gebruikt moet worden en dit consequent aan te houden.
7.3. Performance Voorlopige resultaten voor glassfish geven aan dat het chainen van meerdere interceptors een aanzienlijke performance impact heeft. Bij 1 is het effect nog verwaarloosbaar. Bij een keten van 5 interceptors moet rekening worden gehouden met een verlies in throughput van 40% [zie OAKS]. Bij gebruik van meerdere interceptors dient een controle plaats te vinden op de impact op performance in verhouding tot één of geen interceptors in de keten.
54
Ordina J-Technologies
Cookbook EJB 3.0
8. Gotcha’s In dit hoofstuk staan een aantal zaken, waardoor je verrast zou kunnen worden tijdens de ontwikkeling van EJB 3 componten. Doel van dit hoofdstuk is om hierop te anticiperen en eventueel een workaround of oplossing aan te dragen.
8.1. lib-directory in enterprise application Alle libraries in de lib-directory van een ear-file wordt default beschikbaar gemaakt voor alle componenten. Subdirectories van deze directory worden door de specificatie (EE8.2.1) expliciet uitgesloten. Als je dus een jar file als volgt toevoegt in de enterprise application lib/logging/log4j.jar zal deze dus niet standaard beschikbaar zijn voor de componenten. Vermijd subdirectories binnen de lib-directory van een Enterprise Archive.
8.2. Persistentie 8.2.1. Introductie nieuwe persistence unit Op het moment dat over wordt gegaan van 1 persistence unit naar twee persistence-units zal de volgende code niet meer werken: @PersistenceContext private EntityManager em; De foutmelding is (in glassfish met toplink): Caused by: java.lang.IllegalStateException: Unable to retrieve EntityManagerFactory for unitName null at com.sun.enterprise.util.EntityManagerWrapper.init(EntityManagerWrapper.java:179 ) Default gedrag van de container is dat als er maar één persistence unit is gedefinieerd deze standaard gebruikt wordt. Op het moment dat dit niet meer het geval is moet expliciet de naam van de persistence unit worden opgegeven.
55
Ordina J-Technologies
Cookbook EJB 3.0
Correcte versie is het opgegeven van de persistence unit, waarvoor een entity manager geinjecteerd moet worden. @PersistenceContext(unitName=”cabin”) private EntityManager em; Let hier goed op de unitName! Het attribuut unitName refereert aan de naam van de persistence unit, zoals gedefinieerd in de persistence.xml. Het attribuut name refereert aan de JNDI name van de persistence context.
8.2.2. Date en Calendar velden Date en Calendar velden hebben geen automatische mapping. Bij een entiteit moet altijd aanvullende informatie worden opgegeven bij date en calendar velden over het type. De mogelijke typen (hier uitgedrukt als annotatie) zijn. @Temporal(TemporalType.DATE) @Temporal(TemporalType.TIME) @Temporal(TemporalType.TIMESTAMP) Date bevat alleen het datum-gedeelte, tijd alleen het tijd gedeelte. Timestamp bevat zowel datum als tijd informatie.
8.2.3. Relaties Bij associaties moet altijd aanvullende informatie verschaft worden door middel van de mapping file of annotatie. Afhankelijk van het soort associatie moet het attribuut verrijkt worden met OneToOne, OneToMany, ManyToOne, ManyToMany om aan te geven welk soort associatie het betreft.
8.2.4. Gemanagede objecten In onderstaande code wordt een nieuw onderdeel van een reis aangemaakt en gepersisteerd. Na het aanroepen van de persistoperatie is het object gemanaged. Leg leg = Leg.create(amsterdam, new Date(), rotterdam, new Date()); em.persist(leg); leg.setShip(vliegendeHollander);
56
Ordina J-Technologies
Cookbook EJB 3.0
Vervolgens wordt na de persist het schip toegevoegd. Hierna wordt de transactie gecommit. Tijdens het committen wordt zowel de aankomst en vertrekhaven gezet als het schip. Sterker nog: als er een specifieke flush wordt uitgevoerd, zal er vervolgens een update worden gedaan waarbij het schip voor dat gedeelte van de reis wordt ingevuld. -> Wijzigingen op gemanaged objecten worden bij een commit van een transactie automatisch meegenomen!!!! tran = em.getTransaction(); tran.begin(); Leg retrievedLeg = em.merge(leg); retrievedLeg.setShip(valdez);ran.commit(); EntityManager is gecleared. Geen enkel object is nog gemanaged. Via een merge wordt leg weer gemanaged. Op de leg wordt het schip gewijzigd en er wordt een commit uitgevoerd. Op dat moment wordt automatisch de update van de leg uitgevoerd!.
8.2.5. Gebruik Java 2 Standard Edition Als vergeten wordt om de klasse op te nemen in de persistence.xml en de klasse wordt gebruikt in een associatie dan volgt een vage foutmelding: Exception [TOPLINK-94] (Oracle TopLink Essentials - 2006.4 (Build 060412)): oracle.toplink.essentials.exceptions.DescriptorException Exception Description: Descriptors must have a table name defined. Descriptor: RelationalDescriptor( Oplossing: Voeg de klasse toe aan de persistence.xml
8.2.6. Detached entities en lazy loading Een veel voorkomende fout bij gebruik van persistentie frameworks in het begin is het navigeren over relaties van een entiteit, terwijl de entiteit niet meer gemanaged is. Dit is bijvoorbeeld het geval op het moment dat een EJB component als resultaat van een methode een entiteit teruggeeft aan de webapplicatie. Op het moment dat code binnen de webapplicatie over relaties van die entiteit gaat navigeren kan er een exceptie door de persistence provider gegooid worden op het moment dat deze de relaties door middel van lazy loading wil laden.
57
Ordina J-Technologies
Cookbook EJB 3.0
In onderstaande code kan dus een exceptie komen op het moment dat van een cursus het aantal aanbiedingen wordt opgevraagd.
Welke exceptie gegeven wordt is niet door de specificatie bepaald. Dit is overgelaten aan de persistence provider. Bij Toplink is dit de oracle.toplink.essentials.exceptions. ValidationException met de veelzeggende melding “An attempt was made to traverse a relationship using indirection that had a null Session. This often occurs when an entity with an uninstantiated LAZY relationship is serialized and that lazy relationship is traversed after serialization. To avoid this issue, instantiate the LAZY relationship prior to serialization.”
58
Ordina J-Technologies
Cookbook EJB 3.0
9. Software en Bronnen 9.1. Gebruikte software JDK
http://java.sun.com/javase/downloads/index.jsp
1.5.0.11
ECLIPSE
http://www.eclipse.org
3.3.M4
GLASSFISH
https://glassfish.dev.java.net/public/downloadsindex.html
1 UR1 P01
PLUGIN
https://glassfishplugins.dev.java.net/download/
0.4
WTP
http://download.eclipse.org/webtools/downloads/
1.5.2
DALI
http://www.eclipse.org/dali/
0.5
59
Ordina J-Technologies
Cookbook EJB 3.0
9.2. Geraadpleegde bronnen
60
BURK
Bill Burke, Richard Monson-Hafael, “Enterprise Java Beans 3.0”, O’Reilly, 2006.
CONV
Stephan Janssen , “Java - J2EE Conventions and Guidelines”, Release 1.0.2, 2004
EJB3
Linda DeMichiel and Michael Keith, “JSR 220: Enterprise JavaBeans, Version 3.0: EJB Core Contracts and Requirements “, Sun Microsystems, 02-05-2006.
EJB3S
Linda DeMichiel and Michael Keith, “JSR 220: Enterprise JavaBeans, Version 3.0: EJB 3.0 Simplified API “, Sun Microsystems, 02-05-2006.
JPA
Linda DeMichiel and Michael Keith, “JSR 220: Enterprise JavaBeans, Version 3.0: Java Persistence API”, Sun Microsystems, 02-05-2006.
GOETZ
Brian Goetz, “Java theory and practice: Understanding JTS – the magic behind the scenes”, 1-4-2002, http://www-128.ibm.com/developerworks/java/library/ j-jtp0410/
HORST
Cay Horstmann, Don’t Lie to the EntityManager, 2-7-2006, http://weblogs.java. net/blog/cayhorstmann/archive/2006/07/dont_lie_to_the_1.html
IOCC
Martin Fowler, “Inversion of Control Containers and the Dependency Injection pattern”, 23-01-2004, http://www.martinfowler.com/articles/injection.html.
JEE5
Bill Shanon , “JavaPlatform, Enterprise Edition (Java EE) Specification, v5”, Final release 4/28/06.
KODO
Bea, “Kodo 4.0.0: Developers Guide for JPA/JDO”, 2006.
OAKS
Scot Oaks et al.,“Writing Performant EJB Beans in the Java EE 5 Platform (EJB 3.0) Using Annotations”, september 2006, http://java.sun.com/developer/ technicalArticles/ebeans/ejb_30/.
PEAA
Martin Fowler , “Patterns of Enterprise Application Integration”, Addison-Wesley, 2003. Zie ook http://www.ddj.com/showArticle.jhtml?articleID=184414966
SBS
Rima Patel Sriganesh, Gerald Brose and Micah Silverman, “Mastering Enterprise JavaBeans 3.0”, Juli 2006, http://www.theserverside.com.
Ordina J-Technologies
Cookbook EJB 3.0
10. Bijlagen Inrichting project Gebruikte tools Leverancier
Product
Versie
Sun
Java Development Kit
1.5.0_11-b03
Eclipse
Eclipse
3.3.0 (I20061214-1445)
Eclipse
WTP
2.3.0.v200612211251
Sun
Glassfish Plugin
0.4
Glassfish
Glassfish
V2 (build b41a-beta2)
JDK
Voor EJB 3.0 is de Java Development Kit 5 [JDK ]een vereiste. Stel de JDK (niet de JRE) in als default runtime (Windows – Preferences – Java – Installed JRE’s)
61
Ordina J-Technologies
Cookbook EJB 3.0
Installatie Glassfish
Download Glassfish Application Server [GLASSFISH] en installeer deze: java -Xmx256m -jar [filename].jar Accepteer de license. De jar file wordt uitgepakt in de directory waar het commando wordt uitgevoerd. Configuratie van glassfish wordt gedaan door middel van ant -f setup.xml uit te voeren in de net aangemaakte glassfish directory. Dit zorgt er voor dat de configuratie voor een default domein wordt aangemaakt. Na dit commando kan glassfish zelf niet meer verplaatst worden omdat padinformatie is opjgenomen in de domeinconfiguratie. Als ant nog niet is geïnstalleerd is op het systeem, gebruik dan het commando lib\ant\bin\ ant -f setup.xml.
Installatie eclipse plugin
Download de eclipse plugin [PLUGIN] voor glassfish en installeer deze als eclipse plugin:
Configuratie Eclipse
Vervolgens dient de Glassfish server toegevoegd te worden aan de server runtimes (Windows – Preferences – Server – Installed Runtimes).
62
Ordina J-Technologies
Cookbook EJB 3.0
Kies vervolgens de JDK 5 en selecteer vervolgens het pad naar de home-directory van glassfish.
En kies finish. Als laatste voeg Glassfish als server toe. Ga naar het J2EE perspective naar de view “Servers”. New -> Server. Select Sun Microsystems – Glassfish Java EE 5. Als niets gewijzigd is tijdens de installatie van Glassfish kun je direct finish kiezen.
Project inrichting
Maak een nieuwe enterprise application project aan.
Kies als Target Runtime de GlassFish Java EE 5 server.
63
Ordina J-Technologies
Cookbook EJB 3.0
Kies bij nieuwe J2EE modules de modules die je wilt hebben.
Laatste configuratie loodjes
Verwijder als laatste uit het EJB project de ${src.dir}/META-INF/ejb-jar.xml en sun-ejb-jar. xml (specifieke sun deployment descriptor). Die heb je in de meeste gevallen niet meer nodig aangezien configuratie middels annotaties afdoende is. Op het moment van schrijven is binnen WTP nog geen optimale ondersteuning voor Java EE5. Dit betekent dat de application.xml aangepast moeten worden zodat de juiste namespaces worden gebruikt. In de toekomst zal de ondersteuning van Java EE 5 waarschijnlijk wel verbeteren.
64
Ordina J-Technologies
Cookbook EJB 3.0
Inrichting persistentie project
Maak een nieuw project aan in Eclipse.
Maak hierin twee source paden aan: 1 voor de sources die opgeleverd gaan worden en 1 voor het testen. Kies de eigenschappen van het project. Ga naar het project facets en selecteer ook JPA als project facet. Kies vervolgens bij ‘Targeted runtimes’ voor de Glassfish applicatie server.
65
Ordina J-Technologies
Cookbook EJB 3.0
Persistence providers
De voordelen van de standaardisatie die door de Java Persistence API mogelijk wordt gemaakt, is nu al duidelijk. Er zijn verschillende leveranciers die als Persistence Provider kunnen worden gebruikt. In onderstaande overzicht worden de persistentie providers met hun huidige status (mei 2007) genoemd. Leverancier
Product
Versie
Datum
Open Source
Glassfish Project
Java Persistence Reference Implementation (*) a.k.a. Toplink Essentials
V1 UR1 P01 B02
14-12-2006
J
Hibernate
Hibernate EntityManager
3.2.4 GA
9-5-2007
J
Bea
Kodo
4.1
9-10-2006
N
Apache
OpenJPA (**)
0.9.7
27-4-2007
J
Oracle
Toplink
10.1.3.1.0
11-2006
N
•
De Java Persistence API Implementation is gebaseerd op de code van Toplink, gedoneerd door Oracle en maakt onderdeel uit van de Glassfish Applicatie Server (zie hierna).
Applicatie Servers
In onderstaande tabel is aangegeven welke Persistence Providers default bij applicatieservers worden gebruikt.
66
Leverancier
Applicatie Server
Persistence Provider
Versie
IBM
WebSphere Application Server
Open JPA
EJB3 Feature Pack (alpha)
BEA
Weblogic Server
Kodo 4.1
10
Sun/Oracle
Glassfish
Toplink Essentials
V1 UR 1 P01
JBoss
JBoss Application Server
Hibernate
5.0.0 Beta 2
Apache
Jeronimo
Open JPA
2.0-M5 (29-4-2007)
Oracle
Oracle Application Server (OC4J)
Oracle Toplink
11.1.1.0.0
Sun
Java System Application Server
Toplink Essentials
9.0 Update 1 Patch 1
TmaxSoft
JEUS
?
6
Ordina J-Technologies
Cookbook EJB 3.0
De diverse Persistence Providers kunnen zich nog onderscheiden door het bieden van proprietary uitbreidingen en op het gebied van kwaliteitsattributen zoals performance, caching e.d.
Ontwikkelomgevingen
Voor de annotaties van EJB 3.0 kan al worden volstaan met alleen eclipse. Voor integratie met glassfish kan het beste eclipse 3.2 in combinatie met de Web Tools Platform (WTP) worden gedownload. Kies hier voor wtp all-in-one. Deze optie is inclusief eclipse. Daarnaast is voor integratie met glassfish een extra plugin nodig. Voor persistentie is de volgende aanvullende tooling beschikbaar. Leverancier
Product
Versie
Open Source
Eclipse
Dali JPA Tools
0.5
J
Hibernate
Hibernate tools
3.2 beta 9
J
JBoss
Jboss IDE
2.0.0-Beta2
J
Bea
Workshop for Weblogic Platform 10
-
N
IBM
Rational Application Developer
Nog niet aangekondigd
N
Oracle
JDeveloper
10.1.3.2
N
67