PersistenceContext
PC - PersistenceContext
EM - EntityManager
EMF - EntityManagerFactory
PC zarządzany przez aplikację ma zasięg rozszerzony. PC zarządzany przez kontener może mieć zasięg rozszerzony (tylko dla SFSB) lub zasięg transakcji.
PC rozpoczyna się:
- z chwilą, gdy EM zostanie wywołany w zasięgu aktywnej transakcji JTA (dla EM zarządzanego przez kontener i PC o zasięgu transakcji)
- gdy EM zostanie utworzony (dla EM zarządzanego przez aplikację, PC wówczas zawsze ma zasięg rozszerzony)
- gdy SFSB zostanie utworzony a PC do niego przypięty (dla EM zarządzanego przez kontener i PC o zasięgu rozszerzonym - który może występować tylko w ramach statefula)
Aby zakończyć PC, wywołujemy EM.close(). Nie można tego zrobić na EM zarządzanym przez kontener. Po wywołaniu tej metody, wszystkie metody EM poza isOpen() i getTransaction() wyrzucą IllegalStateException.
PC zarządzany przez kontener musi być JTA. Zarządzany przez aplikację może być JTA lub RESOURCE_LOCAL. (Domyślna wartość transaction-type dla środowisk Java EE to JTA, a dla Java SE - RESOURCE_LOCAL).
Jeżeli wstrzykujemy EM do beana (tzn. PC jest zarządzany przez kontener), to nie będziemy używać EntityTransaction, wykorzystywanego przy RESOURCE_LOCAL.
Jeżeli EM tworzymy ręcznie poprzez EMF.createEntityManager (tzn. PC jest zarządzany przez aplikację), to możemy używać zarówno EntityTransaction jak i JTA.
Jeżeli używamy JTA i utworzyliśmy EM poza zasięgiem transakcji, przed wykonaniem operacji należy wywołać em.joinTransaction().
Podsumowując: PC dzielimy na ContainerManaged/AppManaged, JTA/RESOURCE_LOCAL, TxScoped/Extended.
Domyślny FlushMode (Auto) zapewnia, że przed wykonaniem zapytania zmiany, które mogą go dotyczyć zostaną zrzucone do bazy. Jednak flush nie sprawi, że zmiany z niezatwierdzonej transakcji będą widoczne dla innych transakcji. Nadużywanie flusha ma wpływ na wydajność.
Encje mogą być w jednym z czterech stanów: new, managed, detached, removed. Wszystkie poza new mają persistent identity. Managed i removed są 'associated with PC'.
Remove i lock wymagają non-detached entity. Refresh wymaga managed entity. Merge oczekuje non-removed entity. Persist wymaga po prostu entity (choć wywołana na detached entity może skutkować wyrzuceniem EntityExistsException lub innego PersistenceException). Jak nie dostaną czego chcą, wyplują IllegalArgumentException.
Persist wywołane na removed entity zmieni ją w managed entity.
Persistence.xml jest obowiązkowy. Można w nim zdefiniować wiele PU. Jeden PU odpowiada jednemu datasource. Każdy PU musi mieć nazwę. Jeżeli mamy wiecej niż jeden PU, tworząc EM lub EMF należy podać nazwę konkretnego PU. Encje i klasy embedded muszą być (jawnie lub nie) uwzględnione w persistence.xml jako klasy zarządzane, by były uwzględnione w PU.
Dzięki propagacji PC nie trzeba przekazywać referencji do EM między beanami. Propagacja kontekstu dotyczy PC zarządzanego przez kontener. PC propagowany jest razem z transakcją (tzn. jeżeli transakcja nie jest propagowana, to PC również nie).
JPQL i operacje na bazie
Operacje modyfikujące dane w bazie wymagają transakcji (inaczej wyplują TransactionRequiredException), operacje odczytujące dane nie. Przykładowo update, flush, persist wymagają transakcji, natomiast clear i find już nie. Encje wyciągnięte poza transakcją będą detached (dla PC o zasięgu transakcji) lub managed (dla PC o zasięgu rozszerzonym). Jeżeli find zdarzy się splunąć wyjątkiem to najprawdopodobniej klasa klucza głównego się nie zgadza.
Po wykonaniu executeUpdate załadowane encje, których zmiany dotyczą, nie zostaną odświeżone. Próba wywołania executeUpdate z zapytaniem typu SELECT spowoduje wyrzucenie IllegalStateException. Operacje masowe - DELETE, UPDATE w JPQL mogą się sypnąć na constraintach, rzucając PersistenceException.
Persist będzie kaskadowany przy Cascade.PERSIST i ALL. Remove przy REMOVE i ALL ale tylko dla relacji OneToOne i OneToMany (zachowanie dla pozostałych nie jest określone). Remove na nowej encji nie usunie jej z bazy (jeszcze jej tam nie ma) - ale jeżeli mamy cascade, to spowoduje usunięcie encji wskazywanych przez tę encję.
Zapytania JPQL można zdefiniować za pomocą adnotacji @NamedQuery lub elementu named-query w xml-u. Zapytania sql-owe analogicznie - poprzez @NamedNativeQuery. Wynikiem zapytań mogą być encje, ale można też zdefiniować bardziej skomplikowane mapowania.
Zapytanie "SELECT k FROM Klasa k INNER JOIN k.uczniowie u" zwróci pojedyncze klasy, a nie po 30 sztuk każdej. Co innego, gdybyśmy dodali FETCH.
W zapytaniach można używać konstruktorów. Przykładowo: "SELECT NEW Integer(MAX(k.uczniowie)) FROM Klasa k".
Elementy uwzględnione w ORDER BY muszą występować w klauzuli SELECT i muszą być szeregowalne (tzn. liczby, stringi, znaki lub daty).
SIZE używamy w klauzuli WHERE, natomiast COUNT w klauzuli SELECT. Podzapytań używamy w klauzuli WHERE. ALL, ANY, SOME, EXISTS stosujemy do podzapytań. MEMBER OF stosujemy do kolekcji. MAX, AVG również do kolekcji.
Używając LIKE mamy do dyspozycji dwa wildcardy: _ zastępuje jeden znak, % dowolnie wiele znaków. BETWEEN jest dwustronnie inclusive.
Find() zwraca nulla, jeżeli nic nie znajdzie. GetRefence() wyrzuca EntityNotFoundException - ale getReference nie musi strzelać do bazy (wówczas wyjątek zostanie wyrzucony dopiero przy próbie dobrania się do pola encji).
Contains() zwróci true tylko dla managed entity - dla detached, removed i new zwróci false.
Encje
PP - PersistenceProvider
Encje umieszczamy w roocie PU lub w jarze znajdującym się w classpath PU.
Próba zapisu encji z istniejącym kluczem zakończy się rollbackiem.
Pole trwałe to takie, które nie jest transient ani @Transient. Pole trwałe o "złożonym" typie w encji nie musi być oznaczone jako @Lob, ale musi być serializowalne. (Dokładniej: musi być prymitywem, Stringiem, wrapperem, BigDecimal/Interger, Calendar, java.util/sql.Date, java.sql.Time/Timestamp, enumem, encją, kolekcją encji, klasą @Embeddable, byte[], Byte[], char[], Character[] albo Serializable.)
Określenie pola @Version sprawi, że PP użyje go do zapewnienia optimistic lockingu. @Version powinno być określone na głównej tabeli encji. Dopuszczalne typy: int, Integer, s/Short, l/Long, Timestamp.
@Basic można zawsze dać na polu, ale jest opcjonalna. Ma elementy fetch i optional.
Nazwę tabeli dla encji określamy poprzez @Table("TABLE_NAME") lub poprzez <entity ...> <table name="TABLE_NAME">. Nazw tabel nie nadpisujemy w persistence.xml - do tego służą pliki mapowań. Pliki mapowań to META-INF/orm.xml lub xml-e określone elementem mapping-file w persistence.xml. Domyślna nazwa tabeli przy złączeniach z użyciem JoinTable (gdy nie określono atrybutu name) to połączenie nazw tabel, oddzielonych podkreśleniem, przy czym pierwsza to owning side.
Klucz główny definiujemy w korzeniu hierarchii encji - czy to encja, czy mapped superclass.
Złożone klucze główne można zmapować na dwa sposoby:
- adnotacja @IdClass(SomePKClass.class) na poziomie encji + adnoracje @Id na polach encji odpowiadających polom z SomePKClass;
- adnotacja @EmbeddedId na polu o typie oznaczonym jako @Embeddable.
IdClass musi definiować equals i hashCode zgodne z porównywaniem encji po stronie bazy.
Strategię dziedziczenia określamy poprzez @Inheritance(strategy=[JOINED/SINGLE_TABLE/TABLE_PER_CLASS]). JOINED oznacza, że każda encja, również pośrednia, ma osobną tabelę. TABLE_PER_CLASS nie musi być wspierana w EJB 3.0.
@MappedSuperclass nie może występować w zapytaniach. Niezależnie od strategii, nie posiada osobnej tabeli w bazie. Może natomiast występować w dowolnym miejscu w hierarchii encji.
W relacjach dwustronnych owning side to strona posiadająca klucz obcy i jest to ta strona, po której nie ma mappedBy. W relacjach jednostronnych jedyna istniejąca strona jest owning side. W ManyToMany każda ze stron może być owning side. To operajce wykonywane na owning side mają znaczenie.
@OneToMany oznacza "jeden obiekt klasy, w której znajduje się ta adnotacja do wielu obiektów klasy zdefiniowanej przez pole (kolekcję), na którym się znajduje".
Definiując relacje, atrybut targetEntity jest wymagany jeżeli adnotujemy kolekcję bez generycznego zawężenia.
Gettery/settery dla encji używając dostępu opartego o te metody powinny być publiczne lub chronione. Encja musi mieć bezargumentowy konstruktor. Jeżeli ma być przekazywana przez interfejs zdalny, powinna być również Serializable.
Klucz główny może być prymitywem (ale nie typem numerycznym przybliżonym - double, float), wrapperem, Stringiem, java.sql.Date lub java.util.Date. Dla java.util.Date musi mieć temporal type ustawiony na DATE. (@Temporal musi być określony dla typów java.util.Date i java.util.Calendar.) Musi być serializowalny. Musi definiować equals i hashCode w sposób zgodny ze sposobem porównywania tych wartości w bazie.
Klasy abstrakcyjne mogą być encjami.
Typ dostępu (pola/akcesory) dotyczy całej konkretnej hierarchii encji.
Timery
Chcąc wykonać operację po określonym czasie, używamy Timerów - albo oznaczająć metodę jako @Timeout albo implementując interfejs TimedObject. Bean może mieć tylko jedną metodę timeout.
Callbacki
Callbacki mogą być prywatne, ale nie mogą być statyczne i muszą zwracać void.
Callbacki encji zdefiniowane w listenerach muszą przyjąć Object albo obiekt encji jako argument. Callbacki encji nie powinny wywoływać operacji na EntityManager ani Query, odwoływać się do innych encji ani modyfikować relacji.
Callbacki nie powinny pluć checked exception. Zdefiniowane w klasie, której dotyczą, w większości muszą być bezargumentowe. @PreDestroy w Statelessie wykonuje się w nieokreślonym kontekście transakcji i bezpieczeństwa.
@PostActivate, @PrePassivate - dobre miejsce by inicjować i niszczyć resourcy, które wymagają ręcznej obsługi. Z uwagi na timeouty, w @PreDestroy też warto zająć się niszczeniem resourców. Nieserializowalne pola powinny zostać znullowane w @PrePassivate (dokładniej - pola, które nie są Timerami, SessionContextem, referencjami do beanów, nie są serializowalne. Można pominąć też pola, które staną się "serializowalne" poprzez zastosowanie tych zasad do ich składowych.).
Obowiązki kontenera i restrykcje dla programisty EJB
Kontener musi zapewnić (choćby częściowo) API: JAX-WS, JavaMail, JNDI, JDBC, JAXB i sporo innych (RMI-IIOP, JAXP, Java IDL, EJB 3.0 + JPA, JTA, JMS, JAF, JAXR, JAX_RPC, SAAJ, Connector, Web Services, Web Services Metadata, Common Annotations, StAX).
Kontener nie musi zapewnić load-balancingu ani klastrowania. Nie musi też niczego gwarantować w przypadku awarii serwera.
EJB nie używają interfejsów graficznych. Nie dobierają się do plików za pomocą java.io. Nie ładują bibliotek natywnych. Nie tworzą SecurityManagerów ani Classloaderów. Nie słuchają na gnieździe, ale mogą być klientem gniazda. Nie zajmują się wątkami (w EJB 3.1 ograniczenie to nie dotyczy Singletona z współbieżnością zarządzaną przez beana). Nie zmieniają wartości pól statycznych (dlatego najlepiej od razu oznaczyć je jako final).
Nie używają obiektów związanych z bezpieczeństwem (Policy, Securyty, Provider, Signer, Identity). Nie dobierają się refleksją do elementów prywatnych. Nie używają AWT. Nie pobierają danych z klawiatury. Itd.
Integracja EJB 2.1/3.0
Adnotacja @Init pozwala określić metodę inicjalizującą w 3.0, odpowiadającą metodom createXXX w interfejsach domowych.
Aby wywołać 2.1 z 3.0 można wstrzyknąć referencję do HomeInterface za pomocą adnotacji @EJB, a następnie wywołać na tym obiekcie metodę create().
Aby wywołać 3.0 z 2.1 można użyć adnotacji @RemoteHome i @LocalHome.
Role
Role określone w specyfikacji to: Persistence Provider, Application Assembler, EJB Container Provider, Enterprise Bean Provider, System Administrator, EJB Server Provider, Deployer.
Admin przykładowo konfiguruje LDAP-a.
Deployer mapuje role na grupy użytkowników (wszystkie role - z @RolesAllowed, @DeclareRoles i z XML-a). Ale same role i referencje do ról określaja Bean Provider i Assembler, deployer ich nie zmienia.
Bean Provider i Assembler mogą określać atrybuty transakcji i kolejność interceptorów w deskryptorze wdrożenia.
EJB-JAR jest edytowany przez Bean Providera, Assemblera i Deployera. Jego format jest kontraktem między tą trójką.
Bezpieczeństwo
Metodę można oznaczyć jako unchecked - tzn. każdy ma do niej dostęp. Jeżeli poza elementem unchecked wymieniono również kilka ról, nadal każdy ma do niej dostęp.
@RunAs("SomeRole") na beanie sprawi, że metody wywoływane z jego metod wywołają się z rolą "SomeRole".
Jeżeli role-name w security-role-ref jest taka sama jak nazwa w security-role, element role-link nie jest potrzebny.
Metoda getCallerPrincipal nie zwróci nulla, tylko obiekt reprezentujący 'UNAUTHENTICATED identity'. Obiekt Principal nie zawiera informacji o rolach. Principal.getName zależy od security realm użytego do autentykacji.
Wyjątki
Wyjątki aplikacyjne definiujemy poprzez @ApplicationException. Nie mogą rozszerzać @RemoteException. Wyjątki aplikacyjne są propagowane bez zmian. Runtimy domyślnie są wyjątkami systemowymi. Wyjątki systemowe są opakowywane w EJBException lub w EJBTransactionRolledbackException (gdy metoda wykonywała się w transakcji klienta). Jeżeli bean nie może pozbierać się po checked exception, należy opakować go w EJBException. NoSuchEJBException, EJBException, EJBTransactionRolledbackException i EJBTransactionRequiredException to wyjatki systemowe.
PersistenceExceptions to Runtimy. NonUniqueResultEx (przy getSingleResult) i NoResultEx (wyrzucane przez getSingleResult) nie powodują rollbacka. Pozostałe to EntityExistsEx, EntityNotFoundEx, RollbackEx, OptimisticLockingEx, TransactionRequiredEx.
Wyjątki javax.ejb.CreateException, javax.ejb.RemoveException, javax.ejb.FinderException i ich podklasy to wyjątki aplikacyjne.
Adnotacje i zależności
Rejestrując referencje w ENC (na poziomie klasy) określamy jej nazwę i typ. Na poziomie pola nie jest to konieczne.
Do wstrzykiwania EntityManagerFactory służy @PersistenceUnit. Do wstrzyknięcia EntityManager służy @PersistenceContext.
Dostęp do UserTransaction można uzyskać z JNDI pod nazwą "java:comp/UserTransaction", przez metodę EJBContext.getUserTransaction lub poprzez wstrzyknięcie adnotacją @Resource.
W konstruktorze beana nie dobieramy się do innych beanów. W callbakach statelessa też nie. W callbackach statefula już można. W timeoucie też można.
XML jest zawsze istotniejszy od adnotacji i zazwyczaj je nadpisuje. Może je uzupełniać. W XML-u nie możemy jednak nadpisać wzsystkiego - np. typu beana.
@EJB i @Resource można umieścić na poziomie klasy (dodając tym samym wpisy w ENC beana), pola (wstrzykując zależność) lub metody (jw., na setterze). @RolesAllowed można umieścić na poziomie klasy lub metody (nie przyznajemy uprawnień do pól).
Referencje do destynacji można wstrzyknąć adnotacją @Resource, np. @Resource(mappedName="topic/MyTopic") private javax.jms.Topic myTopic;. Można też to zrobić na poziomie klasy: @Stateless @Resource(name="jms/MyTopic", type=javax.jms.Topic, lookup="topic/MyTopic").
Wstrzykiwanie zależności dostępne jest dla EJB, Interceptorów, Servletów, Filtrów, ServletListenerów, JSF ManagedBeanów, WSEndpointów i Handlerów.
Nie wstrzykujemy do staticów ani pól final. Można wstrzykiwać do pól elementów prywatnych.
Lookup zwraca zawsze nową instancję, chyba że obiekt jest immutable, jest singletonem lub może być współdzielony.
Transakcje
Propagacja transakcji. Notacja:
[metoda_wywołana_w_transakcji;metoda_wywołana_bez_transakcji]
[1-wykona się w istniejącej transakcji, 2-zostanie utworzona nowa transakcja, 0-wykona się bez transakcji,
!-zostanie wyrzucony wyjątek]
- NOT_SUPPORTED [0;0]
- REQUIRED [1;2]
- SUPPORTS [1;0]
- REQUIRES_NEW [2;2]
- MANDATORY [1;!]
- NEVER [!;0]
MANDATORY i NEVER mają wymagania co do kontekstu, w którym są wołane i rzucają wyjątkami, gdy wymagania te nie są spełnione (np. EJBTransactionRequiredException).
NOT_SUPPORTED i REQUIRES_NEW nie zależą od kontekstu, w którym są wywoływane i wykonują się odpowiednio bez transakcji / w nowej transakcji.
REQUIRED i SUPPORTS przyłączają się do istniejącej transakcji; a gdy jej nie ma odpowiednio tworzą nową lub wykonują się bez transakcji.
Klient używający CMT wie, że nie ma co kontynuować transakcji, jeżeli getRollbackOnly zwróci true lub gdy otrzyma EJBTransactionRolledbackException.
Jeżeli SLSB lub MDB nie zakończą transakcji w metodzie biznesowej, kontener ją zrollbackuje. Nie dotyczy to SFSB.
Sposób zarządzania transakcjami określamy adnotacją @TransactionManagement(TransactionManagementType.BEAN) na poziomie beana.
Bean nie może rozpocząć nowej transakcji przed zakończeniem poprzedniej.
Commit transakcji oznaczonej do zrollbackowania spowoduje wyrzucenie wyjątku.
Przy BMT mamy do czynienia z interfejsem UserTransaction. Transakcje obslugujemy metodami getStatus/rollback. W przypadku CMT jest to get/setRollbackOnly. Nie ma problemu, by wywołać CMT z BMT. Po wywołaniu status transakcji możemy spokojnie sprawdzić metodą getStatus.
Wywołanie EJBContext.get/setRollbackOnly gdy nie ma aktywnej transakcji spowoduje wyrzucenie IllegalStateException. UserTransaction.setRollbackOnly również.
Transakcja może zostać zrollbackowana, jeżeli wywołano setRollbackOnly lub wyrzucono wyjątek systemowy lub wyrzucono wyjątek aplikacyjny z rollback=true.
Klient używający CMT wie, że nie ma co kontynuować transakcji, jeżeli getRollbackOnly zwróci true lub gdy otrzyma EJBTransactionRolledbackException.
Jeżeli metoda zostaje nadpisana w klasie dziedziczącej, atrybuty transakcji z klasy bazowej są ignorowane. Tzn. jeżeli po przesłonięciu nie definiujemy atrybutu, to jest REQUIRED.
W sytuacji, gdy adnotacjami określono TransactionManagement na konkretniej metodzie, a w xml-u określono TManagement dla wszystkich metod beana - pod uwagę jest brana wartość z xml-a.
Beany
Klasy beanów muszą być publiczne, top-level, nieabstrakcyjne, niefinalne.
Dwa kolejne lookupy statefula przez tego samego klienta spowodują utworzenie dwóch różnych instancji.
Dwie instancje statelessów przejdą test na identyczność (equals). Statefulli już nie.
Przekazywanie argumentów poprzez zdalne interfejsy jest zgodne z Java RMI-IIOP. Nawet, jeżeli wykorzystywane są lokalnie. Przy interfejsach @Remote przekazujemy argumenty przez wartość, przy @Local przez referencję.
Jeżeli bean implementuje więcej niż jeden interfejs (pomijając Serializable, Externalizable i interfejsy z pakietu javax.ejb), wszystkie interfejsy muszą być zaadnotowane jako @Local lub @Remote. Jeżeli ma tylko jeden interfejs, nie musi mieć on adnotacji - domyślnie przyjmuje się, że jest to interfejs lokalny. Interfejs nie może być jednocześnie oznaczony jako @Local i @Remote.
Adnotacje można spokojnie umieszczać na klasie, po której bean dziedziczy.
Nazwa beana jest brana z nazwy klasy, jeżeli nie określono atrybutu name adnotacji @Stateful/@Stateless.
Element <ejb-ref-name> odpowiada @EJB(name=...). <injection-target-name> odpowiada nazwie pola. @EJB(beanName=...) odpowiada elementowi <ejb-link>. Typ zmiennej odpowiada interfejsowi, np <local>.
Wywołanie metodę na usuniętej instancji statefula spowoduje wyrzucenie NoSuchEJBException.
Interfejs SessionSynchronization przeznaczony jest dla statefuli.
Stan statefula jest utrzymywany między wywołaniami metod i transakcjami. Pola interceptortów statefula są częścią jego stanu.
Metody wystawione przez WS nie powinny zwracać referencji, które nie mają sensu poza JVM, w tym interfejsów lokalnych/zdalnych ani timerów.
Metody getEJBObject() i getEJBLocalObject() są przesztarzałe. Referencję do beana uzyskamy metodą getBusinessObject().
MDB
Chcąc się dobrać do MessageDrivenContext, po prostu deklarujemy do niego zależność.
MDB JMS-owe implementują javax.jms.MessageListener, z metodą onMessage(Message message). Metoda onMessage nie może deklarować checked exceptions (RemoteException to też checked exception).
Można zawęzić rodzaj komunikatów odbieranych przez jMS poprzez message selector declarations. Przypisane beana do kolejki lub tematu odbywa się poprzez atrybut activationConfig adnotacji @MessageDriven.
Ustawienie subscriptionDurability=Durable na temacie (topic) sprawi, że MDB nie przegapi żadnej wiadomości, nawet w przypadku awarii serwera. Jeżeli chodzi o kolejki (queue), wysyłane komunikaty podczas awarii serwera zostaną dostarczone po restarcie.
Przy CMT i REQUIRED otrzymanie komunikatu zostanie zrollbackowane, jeżeli metoda wyrzuci wyjątek (jest częścią transakcji).
Kontener zajmuje się potwierdzeniem odbioru komunikatu, nawet przy BMT.
Instancja MDB obsługuje co najwyżej jeden wątek na raz. Kolejność komunikatów nie musi zostać zachowana.
Można określić adres zwrotny w atrybucie JMSReplyTo komunikatu.
Źródła
Specyfikacja
Enterprise JavaBeans 3.1, Andrew Lee Rubinger and Bill Burke
EJB 3 in Action, Debu Panda, Reza Rahman, Derek Lane
Mikalai Zaikin SCBCD Study Guide
Pokazywanie postów oznaczonych etykietą EJB. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą EJB. Pokaż wszystkie posty
sobota, 5 stycznia 2013
środa, 7 listopada 2012
EJB 3.1 [8]
Timer Service
A timer is associated with the bean that created it and it calls it's ejbTimeout(Timer timer)/@Timeout method when it goes off.
Since EJB 3.1 there is a ScheduleExpression class that allows to define when timer should be fired. There is also @Schedule annotation, that alows to achieve the same in a declarative manner.
EJB must implement TimedObject interface to use Timer Service.
TimerService can be accessed with sessionContext.getTimerService() method. You can create a timer with timerService.createXxxTimer() call (it can create SingleAction, Interval ands Calendar timers).
Alternatively, you can do it in declarative manner with @Schedule annotation:
Timers are persistent - they will be restored if the system crashes. It isn't clear what will happen with the interval timers that would go off several times while the system is down (they may catch up just after the startup).
Timers can be cancelled. To reschedule a timer you cancel it and create a new one. Timer created in the rolledback transaction scope will get uncreated.
Web Services
WIth JAX-RPC there is a lot to define (WSDL, JAX_RPC mapping file and webservices.xml). with JAX-WS all you need is @WebService and @WebMethod annotations.
When there is no @WebMethod annotation - all methods of bean annotated with @WebService are exposed.
@SOAPBinding annotation allows you to customize WS: style: DOCUMENT/RPC, use: LITERAL/ENCODED(do not use), parameterStyle: BARE/WRAPPED.
@WebParam can be placed on parameters (with name and mode: IN/OUT/INOUT attributes). @WebResult is the same for return value.
@OneWay annotation for methods that do not return response allows server to call it asynchronously (if it wishes to).
@WebService annotation has endpointInterface attribute, that allows to separate interface from implementation. We plece @WebService annotation on both the interface and the implementation then.
There is a @WebServiceRef that allows to inject a service class or an endpoint (the annotation would take the service class as an argument then).
A timer is associated with the bean that created it and it calls it's ejbTimeout(Timer timer)/@Timeout method when it goes off.
Since EJB 3.1 there is a ScheduleExpression class that allows to define when timer should be fired. There is also @Schedule annotation, that alows to achieve the same in a declarative manner.
EJB must implement TimedObject interface to use Timer Service.
TimerService can be accessed with sessionContext.getTimerService() method. You can create a timer with timerService.createXxxTimer() call (it can create SingleAction, Interval ands Calendar timers).
Alternatively, you can do it in declarative manner with @Schedule annotation:
Timers are persistent - they will be restored if the system crashes. It isn't clear what will happen with the interval timers that would go off several times while the system is down (they may catch up just after the startup).
Timers can be cancelled. To reschedule a timer you cancel it and create a new one. Timer created in the rolledback transaction scope will get uncreated.
Web Services
WIth JAX-RPC there is a lot to define (WSDL, JAX_RPC mapping file and webservices.xml). with JAX-WS all you need is @WebService and @WebMethod annotations.
When there is no @WebMethod annotation - all methods of bean annotated with @WebService are exposed.
@SOAPBinding annotation allows you to customize WS: style: DOCUMENT/RPC, use: LITERAL/ENCODED(do not use), parameterStyle: BARE/WRAPPED.
@WebParam can be placed on parameters (with name and mode: IN/OUT/INOUT attributes). @WebResult is the same for return value.
@OneWay annotation for methods that do not return response allows server to call it asynchronously (if it wishes to).
@WebService annotation has endpointInterface attribute, that allows to separate interface from implementation. We plece @WebService annotation on both the interface and the implementation then.
There is a @WebServiceRef that allows to inject a service class or an endpoint (the annotation would take the service class as an argument then).
piątek, 2 listopada 2012
EJB 3.1 [7]
Transactions
@TransactionAttribute(TransactionAttributeType.REQUIRED)
This annotation can be placen on a bean or on a method. It takes several values (though REQUIRED is the default one):
- NOT_SUPPORTED - Annotated method will alwes run outside transaction scope.
- SUPPORTS - If there is a transaction, it joins it. Otherwise it runs without one.
- REQUIRED - If there is a transaction, it joins. Otherwise it starts a new one.
- REQUIRES_NEW - It always starts a new transaction.
- MANDATORY - If there is no transaction, it throws an exception.
- NEVER - If there is a transaciton, it throws an exception.
MDBs can use only NOT_SUPPORTED (no transaction) and REQUIRED (new transaction). MDB method cannot be invoked within transaction context.
With MDB, the message is a part of the transaction only in case of CMT - this means JMS provider will redeliver it. For BMT the JMS provider will not know of the failure (unless it relies on acknowledgements).
EJB endpoints do not propagate transactions, therefore MANDATORY is forbidden in this case (it can change in the future).
Transactions and PersistenceContext
EM - EntityManager. PC - PersistentContext. SFSB - StateFullSessionBean
- TX-scoped EM called outside transaction will create PC for the method duration. Entities will then become detached.
- Tx-scoped EM called within transaction will create new PC (if there isn't one already) and associate it with transaciton. If PC exists, it will use it. (It applies to injected EMs.)
- If an EJB with tx-scoped PC calls SFSB with extended PC, an error is thrown.
- If SFSB with extended PC calls an EJB with tx-scoped one, extended context is propagated.
- SFSB with extended PC, calling non-injected SFSB with extended PC, will cause an error. For injected one - PC will be shared.
You can specify transaction isolation level using JDBC API (to do so lookup dataSource, then use getConnection method): connection.setTransacitonIsolation(Connection.TRANSACITON_SERIALIZABLE);
If there are many rsources in one transaciton, each can have different isolation level. But for one resource, this level must remain the same in any number of transacitons.
Locking
@Version annotation allows you to use OptimisticLocking. The annotation is placed on a dedicated field, that gets incremented with each update. If at the end of transaction version value is greater than at the beginning - it means that entity has bean changed by someone else - and OptimisticLockingException is thrown.
You can also lock entities manually - EntityManager has method lock (Object entity, LockModeType type), with LockModeTypes: READ and WRITE. It doesn't have to work on entities without @Version field.
UserTransaction
When managing transactins programatically, the most important interface is javax.transaction.UserTransaction. EJB server has to support UserTransaction, though it does not have to support the rest of the JTA. Main methods of UserTransaction:
- begin - throws IllegalStateException if there is already a transaction connected with the thread,
- commit - can throw HeuristicRollbackException or HeuristicMixedException if one of the resources has individually decided to rollback (/commit) transaction,
- getStatus - returns int,
- rollback - can throw SecurityException if the thread is not entitled to rollback transaction,
- setRollbackOnly,
- setTransactionTimeout(seconds) - can be called only after begin. If 0 is passed, the default value will be used.
With CMT you cannot use UserTransaction. All you need here is EjbContext.get/setRollbackOnly.
With BMT you should use UserTransaction.getStatus/rollback instead.
System and application exceptions
SystemExceptions are runtimes (including EjbException) and java.rmi.remoteException.
They can be turned into ApplicationExceptions via @ApplicationException annotation (it has rollback attribute that defaults to false).
SystemException thrown by an EJB always cause a rollback (both with standard and callback methods). The container will discard the EJB after that. If a SFSB is discarded, another call would end in NoSuchEJBException (runtime).
SFSB and SessionSynchronization
If a statefull bean implements SessionSynchronization interface, it has a new lifecycle state - Transactional Method-Ready (the bean knows it's in transaction). THere are new callbacks: afterBagin, beforeCompletion and afterCompletion(boolean comitted).
If a stateful is in transaction and someone calls method with requiresNew/notSupported/never attribure, an error will be thrown.
Calling a @Remove annotated method inside a transaction will also result in an error.
The @beforeCompletion method is not called before rollback. The afterCompletion method is called before both rollback and commit.
Interceptors
Create a class MyAuditor. Write a method taking a InvocationContext argument, returning Object result. Annotate it with @javax.interceptor.AroundInvoke. Then bind it to a bean (or to a method) with @Interceptors(MyAuditor) annotation.
Alternatively, use xml:
Method-params would be needed in case the method is overloaded. If you leave method-name and method-params tags out, the interceptor will be applied to all bean methods. if you use wildcard * as ejb-name, the interceptor will become global. Then, there are @ExcludeDEfaultInterceptors and @ExcludeClassInterceptors annotations.
InvocationContext argument has methods: getTarget (the bean), getMethod, get/setParameters, getContextData(used to pass information between chained interceptors), proceed(causes the target method to execute), getTImer.
You can use @Resource, @EJB and @PersistenceContext annotations in an interceptor, just like if it was a bean.
You can create callback interceptor by putting callback annotation on an interceptor method.
You can also declare @AroundInvoke method in the bean itself. It will be the most "inner" one, thus invoked as the last one.
@TransactionAttribute(TransactionAttributeType.REQUIRED)
This annotation can be placen on a bean or on a method. It takes several values (though REQUIRED is the default one):
- NOT_SUPPORTED - Annotated method will alwes run outside transaction scope.
- SUPPORTS - If there is a transaction, it joins it. Otherwise it runs without one.
- REQUIRED - If there is a transaction, it joins. Otherwise it starts a new one.
- REQUIRES_NEW - It always starts a new transaction.
- MANDATORY - If there is no transaction, it throws an exception.
- NEVER - If there is a transaciton, it throws an exception.
MDBs can use only NOT_SUPPORTED (no transaction) and REQUIRED (new transaction). MDB method cannot be invoked within transaction context.
With MDB, the message is a part of the transaction only in case of CMT - this means JMS provider will redeliver it. For BMT the JMS provider will not know of the failure (unless it relies on acknowledgements).
EJB endpoints do not propagate transactions, therefore MANDATORY is forbidden in this case (it can change in the future).
Transactions and PersistenceContext
EM - EntityManager. PC - PersistentContext. SFSB - StateFullSessionBean
- TX-scoped EM called outside transaction will create PC for the method duration. Entities will then become detached.
- Tx-scoped EM called within transaction will create new PC (if there isn't one already) and associate it with transaciton. If PC exists, it will use it. (It applies to injected EMs.)
- If an EJB with tx-scoped PC calls SFSB with extended PC, an error is thrown.
- If SFSB with extended PC calls an EJB with tx-scoped one, extended context is propagated.
- SFSB with extended PC, calling non-injected SFSB with extended PC, will cause an error. For injected one - PC will be shared.
You can specify transaction isolation level using JDBC API (to do so lookup dataSource, then use getConnection method): connection.setTransacitonIsolation(Connection.TRANSACITON_SERIALIZABLE);
If there are many rsources in one transaciton, each can have different isolation level. But for one resource, this level must remain the same in any number of transacitons.
Locking
@Version annotation allows you to use OptimisticLocking. The annotation is placed on a dedicated field, that gets incremented with each update. If at the end of transaction version value is greater than at the beginning - it means that entity has bean changed by someone else - and OptimisticLockingException is thrown.
You can also lock entities manually - EntityManager has method lock (Object entity, LockModeType type), with LockModeTypes: READ and WRITE. It doesn't have to work on entities without @Version field.
UserTransaction
When managing transactins programatically, the most important interface is javax.transaction.UserTransaction. EJB server has to support UserTransaction, though it does not have to support the rest of the JTA. Main methods of UserTransaction:
- begin - throws IllegalStateException if there is already a transaction connected with the thread,
- commit - can throw HeuristicRollbackException or HeuristicMixedException if one of the resources has individually decided to rollback (/commit) transaction,
- getStatus - returns int,
- rollback - can throw SecurityException if the thread is not entitled to rollback transaction,
- setRollbackOnly,
- setTransactionTimeout(seconds) - can be called only after begin. If 0 is passed, the default value will be used.
With CMT you cannot use UserTransaction. All you need here is EjbContext.get/setRollbackOnly.
With BMT you should use UserTransaction.getStatus/rollback instead.
System and application exceptions
SystemExceptions are runtimes (including EjbException) and java.rmi.remoteException.
They can be turned into ApplicationExceptions via @ApplicationException annotation (it has rollback attribute that defaults to false).
SystemException thrown by an EJB always cause a rollback (both with standard and callback methods). The container will discard the EJB after that. If a SFSB is discarded, another call would end in NoSuchEJBException (runtime).
SFSB and SessionSynchronization
If a statefull bean implements SessionSynchronization interface, it has a new lifecycle state - Transactional Method-Ready (the bean knows it's in transaction). THere are new callbacks: afterBagin, beforeCompletion and afterCompletion(boolean comitted).
If a stateful is in transaction and someone calls method with requiresNew/notSupported/never attribure, an error will be thrown.
Calling a @Remove annotated method inside a transaction will also result in an error.
The @beforeCompletion method is not called before rollback. The afterCompletion method is called before both rollback and commit.
Interceptors
Create a class MyAuditor. Write a method taking a InvocationContext argument, returning Object result. Annotate it with @javax.interceptor.AroundInvoke. Then bind it to a bean (or to a method) with @Interceptors(MyAuditor) annotation.
Alternatively, use xml:
Method-params would be needed in case the method is overloaded. If you leave method-name and method-params tags out, the interceptor will be applied to all bean methods. if you use wildcard * as ejb-name, the interceptor will become global. Then, there are @ExcludeDEfaultInterceptors and @ExcludeClassInterceptors annotations.
InvocationContext argument has methods: getTarget (the bean), getMethod, get/setParameters, getContextData(used to pass information between chained interceptors), proceed(causes the target method to execute), getTImer.
You can use @Resource, @EJB and @PersistenceContext annotations in an interceptor, just like if it was a bean.
You can create callback interceptor by putting callback annotation on an interceptor method.
You can also declare @AroundInvoke method in the bean itself. It will be the most "inner" one, thus invoked as the last one.
niedziela, 28 października 2012
EJB 3.1 [6]
Entity callbacks and listeners
You can define callback for entity lifecycle events in two ways:
1. In entity class you define no-arg, void, checkedExceptionless method and annotate it with one of:
@[Pre/Post][Persist/Update/Remove] or @PostLoad.
2. You define this method in another class (MyFancyListener), except this time it would take and Object argument (representing the entity). The class needs a public, no-arg constructor. Next, you apply listener on entity by @EntityListeners annotation:
In the mapping xml you can define default listeners for all entities:
Then, you can exlude them for a specific entity with @ExcludeDefaultListeners.
Entity inherits parent entity interceptors, but those can also be excluded with @ExcludeSuperclassListeners.
Listeners execute from outside - first default ones, then parent and then those defined for an entity itself.
Security
While creating InitialContext to connect to EJB, you can pass properties containing username (Context.SECURITY_PRINCIPAL) and password (Context.SECURITY_CREDENTIALS).
You can use annotations like @RolesAllowed({"EMPLOYEE"}) and @PermitAll on methods.
Roles are defined either in deployment descriptor () or on the bean (@javax.annotation.security.DeclareRoles({"EMPLOYEE", "CLIENT"))).
Access can be restricted programatically by EJBContext methods getCallerPrincipal() and isCallerInRole().
There is also @RunAs annotation. It can be useful e.g. when MDB needs to access restricted method. (There is no user in context for MDBs).
JNDI and ENC
Beginning with eversion 3.1, all stateless and statefull beans have to be available in global JNDI context under name( elements in '[]' are optional):
java:global[/appOrEARName]/JARorWARName/beanName [!fullInterfaceName]
Example:
Beans have EnterpriseNamingContext. It's something like a map with references to other beans and resources. Before looking up a bean, you need to register it in current bean ENC (to register more EJBs use @EJBs annotation):
and then feel free to grab it:
Notice that when using ejbContext (as opposed to InitialContext) you can omit "java:comp/env" part of JNDI name.
You don't have to register anything if you use @EJB annotation directly on a field or it's setter - the ENC will get populated automatically.
If you specify name attribute of @EJB, the reference will be bound under this name. Otherwise, the name defaults to fullBeanName/fieldName.
Knowing this name, you can override it in xml, like this:
Other than name, @EJB has also mappedName and lookup attributes. MappedName is JNDI name used by provider. Lookup is the same (just vendor independent) - it came with the JNDI name standarization in version 3.1.
EJB name (name attribute of @Statefull/@Stateless or <ejb-name> in xml) has to be unique in EJB-JAR. In EARs there can be duplicates - if this is the case, use #:
Same applies to @PersistenceUnit injection:
When we use @EJB annotation without both mappedName and lookup, the container will:
- Look for an EJB with matching interface in EJB-JAR. if it finds more than one, a deployment exception will be thrown.
- Search other EJB-JARs.
- Search other global EJB-JAR deployments.
Other than EJBs, you can register resources in ENC:
Resources that are Strings or primitives are treated as environmental entries.
You can define callback for entity lifecycle events in two ways:
1. In entity class you define no-arg, void, checkedExceptionless method and annotate it with one of:
@[Pre/Post][Persist/Update/Remove] or @PostLoad.
2. You define this method in another class (MyFancyListener), except this time it would take and Object argument (representing the entity). The class needs a public, no-arg constructor. Next, you apply listener on entity by @EntityListeners annotation:
In the mapping xml you can define default listeners for all entities:
Then, you can exlude them for a specific entity with @ExcludeDefaultListeners.
Entity inherits parent entity interceptors, but those can also be excluded with @ExcludeSuperclassListeners.
Listeners execute from outside - first default ones, then parent and then those defined for an entity itself.
Security
While creating InitialContext to connect to EJB, you can pass properties containing username (Context.SECURITY_PRINCIPAL) and password (Context.SECURITY_CREDENTIALS).
You can use annotations like @RolesAllowed({"EMPLOYEE"}) and @PermitAll on methods.
Roles are defined either in deployment descriptor (
Access can be restricted programatically by EJBContext methods getCallerPrincipal() and isCallerInRole().
There is also @RunAs annotation. It can be useful e.g. when MDB needs to access restricted method. (There is no user in context for MDBs).
JNDI and ENC
Beginning with eversion 3.1, all stateless and statefull beans have to be available in global JNDI context under name( elements in '[]' are optional):
java:global[/appOrEARName]/JARorWARName/beanName [!fullInterfaceName]
Example:
Beans have EnterpriseNamingContext. It's something like a map with references to other beans and resources. Before looking up a bean, you need to register it in current bean ENC (to register more EJBs use @EJBs annotation):
and then feel free to grab it:
Notice that when using ejbContext (as opposed to InitialContext) you can omit "java:comp/env" part of JNDI name.
You don't have to register anything if you use @EJB annotation directly on a field or it's setter - the ENC will get populated automatically.
If you specify name attribute of @EJB, the reference will be bound under this name. Otherwise, the name defaults to fullBeanName/fieldName.
Knowing this name, you can override it in xml, like this:
Other than name, @EJB has also mappedName and lookup attributes. MappedName is JNDI name used by provider. Lookup is the same (just vendor independent) - it came with the JNDI name standarization in version 3.1.
EJB name (name attribute of @Statefull/@Stateless or <ejb-name> in xml) has to be unique in EJB-JAR. In EARs there can be duplicates - if this is the case, use #:
Same applies to @PersistenceUnit injection:
When we use @EJB annotation without both mappedName and lookup, the container will:
- Look for an EJB with matching interface in EJB-JAR. if it finds more than one, a deployment exception will be thrown.
- Search other EJB-JARs.
- Search other global EJB-JAR deployments.
Other than EJBs, you can register resources in ENC:
Resources that are Strings or primitives are treated as environmental entries.
wtorek, 16 października 2012
EJB 3.1 [4] - Persistence
Interfejs Query:
- getResultList(); getSingleResult();
- executeUpdate();
- get/setMaxResults(int maxResults);
- get/setFirstResult(int startPosition);
- setHint(); getHints();
- getParameter/s(); getParameterValue();
- setParameter(); //na 50 sposobów
- get/setFlushMode();
- get/setLockType();
- T unwrap(Class);
Interfejs EntityManager:
- createQuery();
- createNamedQuery();
- createNativeQuery();
W zapytaniach używamy atrybutu name encji (domyślnie nazwa klasy).
Parametry mogą być numeryczne (?1) lub nazwane (:name). Ustawiajac parametry typu Calendar/Date przekazujemy TemporalType.
Przykład zapytania JPQL:
"object" jest opcjonalnym archaizmem, "as" też jest opcjonalne. Można zapisać:
Jeżeli getResultList() wyciąga więcej niż jedną kolumnę lub encję, wyniki w zwracanej liście zostaną zagregowane w tablice Object[]:
Można zrobić niejawnego joina nawigując po polach:
Nie można nawigować po polach klasy, która nie jest encją (czyli która jest zapisana w bazie jako bytestream). Po embedable można.
Można w zapytaniu wywołać konstruktor:
Istnieje (nieintuicyjny) operator IN pozwalający przypisać identyfikator elementom kolekcji
To samo możemy osiągnąć joinem:
Jak chcemy zaciągnąć powiązane encje używamy join fetch:
W części WHERE można użyć operatorów arytmetycznych (ale w części SELECT już nie).
Nie można konkatenować Stringów plusem. Jest do tego funkcja concat().
Można porównywać Stringi poprzez <, <=, choc specyfikacja nie określa, jak będą porównywane. Nie można użyć between na Stringach. Between jest inclusive. Porównywanie dwóch encji porówna je po kluczach głównych. Wartość wyrażenia where null in (1,2) jest nieprzewidywalna. Poniższe zapytanie nie wyciągnie osób, które nie mają adresu:
Poniższe zapytanie zwóci listę z nullami, jeżeli istnieją osoby bez adresu:
Można używać "is empty" oraz "member of".
Wyrażenia regularne: % - dowolna ilość znaków, _ - jeden znak, \ - escapowanie.
Dostępne funkcje: lower, upper, trim(leading, trailing, both),concat, lenght, locate(str, str, start), substring(str, start, length), abs, sqrt, mod,current_date, current_time, current_timestamp
Agregujące: count, max, min, avg, sum
Pole, po którym sortujemy (order by) musi należeć do wyciąganej encji i znajdować się w części SELECT. Poniższe zapytanie jest błędne:
Można wykonywać operacje masowe: [update where]/[delete from where]. Przedtem warto wykonać em.flush() i em.clear() by nie rozsynchronizować encji.
W encji można zdefiniować nazwane zapytania i mapowania dla zwracanych wyników. Przykłady:
- getResultList(); getSingleResult();
- executeUpdate();
- get/setMaxResults(int maxResults);
- get/setFirstResult(int startPosition);
- setHint(); getHints();
- getParameter/s(); getParameterValue();
- setParameter(); //na 50 sposobów
- get/setFlushMode();
- get/setLockType();
-
Interfejs EntityManager:
- createQuery();
- createNamedQuery();
- createNativeQuery();
W zapytaniach używamy atrybutu name encji (domyślnie nazwa klasy).
Parametry mogą być numeryczne (?1) lub nazwane (:name). Ustawiajac parametry typu Calendar/Date przekazujemy TemporalType.
Przykład zapytania JPQL:
"object" jest opcjonalnym archaizmem, "as" też jest opcjonalne. Można zapisać:
Jeżeli getResultList() wyciąga więcej niż jedną kolumnę lub encję, wyniki w zwracanej liście zostaną zagregowane w tablice Object[]:
Można zrobić niejawnego joina nawigując po polach:
Nie można nawigować po polach klasy, która nie jest encją (czyli która jest zapisana w bazie jako bytestream). Po embedable można.
Można w zapytaniu wywołać konstruktor:
Istnieje (nieintuicyjny) operator IN pozwalający przypisać identyfikator elementom kolekcji
To samo możemy osiągnąć joinem:
Jak chcemy zaciągnąć powiązane encje używamy join fetch:
W części WHERE można użyć operatorów arytmetycznych (ale w części SELECT już nie).
Nie można konkatenować Stringów plusem. Jest do tego funkcja concat().
Można porównywać Stringi poprzez <, <=, choc specyfikacja nie określa, jak będą porównywane. Nie można użyć between na Stringach. Between jest inclusive. Porównywanie dwóch encji porówna je po kluczach głównych. Wartość wyrażenia where null in (1,2) jest nieprzewidywalna. Poniższe zapytanie nie wyciągnie osób, które nie mają adresu:
Poniższe zapytanie zwóci listę z nullami, jeżeli istnieją osoby bez adresu:
Można używać "is empty" oraz "member of".
Wyrażenia regularne: % - dowolna ilość znaków, _ - jeden znak, \ - escapowanie.
Dostępne funkcje: lower, upper, trim(leading, trailing, both),concat, lenght, locate(str, str, start), substring(str, start, length), abs, sqrt, mod,current_date, current_time, current_timestamp
Agregujące: count, max, min, avg, sum
Pole, po którym sortujemy (order by) musi należeć do wyciąganej encji i znajdować się w części SELECT. Poniższe zapytanie jest błędne:
Można wykonywać operacje masowe: [update where]/[delete from where]. Przedtem warto wykonać em.flush() i em.clear() by nie rozsynchronizować encji.
W encji można zdefiniować nazwane zapytania i mapowania dla zwracanych wyników. Przykłady:
niedziela, 14 października 2012
EJB 3.1 [3] - Persistence
Można wyróżnić 7 typów relacji między encjami (1:1, 1:N, N:1, N:N, 1- i 2-kierunkowe). Relacje dwukierunkowe 1:N i N:1 niczym się nie różną.
- 1:1 [->]
Pracownik ma adres. Tabela EMPLOYEE ma kolumnę ADDRESS_ID.
@JoinColumn: name (kolumna w tabeli EMPLOYEE, na której bazuje relacja), referencedColumnName (kolumna w tabeli ADDRESS, jeżeli jest inna niż klucz główny (musi być unique))
Adnotacja @JoinColumn jest opcjonalna (można ją pominąć, polegając na domyślnych wartościach). Jeżeli łączymy po kilku kolumnach, używamy @JoinColumns.
@OneToOne: targetEntity, cascade, fetch, optional, mappedBy(dla relacji dwukierunkowych), orphanRemoval(default false - określa, czy usunięcie Pracownika powinno sposodować usunięcie Adresu).
- 1:1 [<->]
Pracownik ma komputer. Komputer ma właściciela. COMPUTER ma kolumnę OWNER_ID.
W relacjach dwukierunkowych mamy do czynienia z encją nadrzędną i podrzędną. Encja podrzędna jest właścicielem relacji (owning side) i posiada klucz obcy do encji nadrzędnej (inverse). Strona inverse posiada adnotację z atrybutem mappedBy (wskazującym na pole w klasie właściciela definiujące relację). W tym przypadku właścicielem relacji jest komputer. (Pilnowanie, by relacja była poprawnie określona po obu stronach (computer.setOwner() i employee.setComputer()) jest zadaniem programisty.
- 1:N [->]
Pracownik ma telefony. Tabela PHONE ma kolumnę OWNER_ID. Model wygląda następująco:
Tym razem @JoinColumn umieszczona w klasie Employee wskazuje na kolumnę w tabeli Phone.
- N:1 [->]
Rejs ma przypisany statek. Tabela CRUISE ma kolumnę SHIP_ID.
- 1:N [<->]
Rejs ma rezerwacje. Tabela RESERVATION ma kolumnę CRUISE_ID.
Właścicielem relacji jest Rezerwacja. @ManyToOne nie może mieć atrybuty mappedBy.
- N:N [<->]
Rezerwacje są powiązane z klientami. Tabela łącząca RESERVATION_CUSTOMER ma kolumny RESERVATION_ID oraz CUSTOMER_ID.
Właścicielem jest Rezerwacja i to ona posiada adnotację @JoinTable.
- N:N [->]
Rezerwacje są powiązane z kabinami. Tabela łącząca CABIN_RESERVATION ma kolumny RESERVATION_ID i CABIN_ID.
Poza Collection i Set można też używać List i Map. Używając listy możemy dodać adnotację @OrderBy("name ASC, surname ASC"). Używając mapy określamy, która właściwość klasy ma być użyta jako klucz w mapiepoprzez @MapKey(name="number"). Bez tej adnotacji, jako klucz użyty zostanie klucz główny.
Polegając na domyślnych wartościach, można ograniczyć metadane relacji do jednej z adnotacji @[One/Many]To[One/Many], bez atrybutów.
Dla konkretnej kolumny lub relacji możemy określić fetchType jako EAGER lub LAZY. Pola z FetchType.LAZY zostaną pobrane z bazy dopiero przy próbie odwołania się do ich wartości (uwaga - fetchType to tylko wskazówka!). Jeżeli pierwsze odwołanie będzie miało miejsce w momencie, gdy encja jest odpięta od EM (detached), większość dostawców wyrzuci pewien rodzaj LazyInitializationException (choć specyfikacja tego nie określa). Rozwiązaniem jest znawigowanie do konkretnego pola, póki encja jest wciąż zarządzana lub jawne wyciągnięcie powiązanej encji w zapytaniu (join fetch).
Operacje kaskadowe
Adnotacje @[One/Many]To[One/Many] mają atrybut cascade, przyjmujący wartości: enum CascadeType{ALL, PERSIST, MERGE, REMOVE, REFRESH} (oraz DETACH od JEE6).
Przykład: @OneToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})
CascadeType.PERSIST - przy zapisie pracownika zapisany zostanie również jego adres.
CascadeType.MERGE - przy mergowaniu pracownika, zmergujemy również jego telefony. Jeżeli w międzyczasie dodaliśmy pracownikowi telefon, zostanie on wstawiony do bazy. Jeżeli usunęliśmy jeden z jego telefonów - nie zostanie usunięty (aby uzyskać taki efekt należałoby użyć orphanRemoval).
CascadeType.REMOVE - usuwając pracownika, usuniemy jego adres.
...
Hierarchia encji
Encje mogą tworzyć hierarchię. Przykładowo: Employee extends Customer extends Person. Możemy ją zmapować na 3 sposoby:
1. Tabela na hierarchię (InheritanceType.SINGLE_TABLE)
Jedna tabela zawiera wszystkie dane. Najprostsze, najwydajniejsze, z tym że musimy zrezygnować z constraintów not-null.
W tabeli musimy zdefiniować kolumnę discriminator - określającą, jakim typem osoby jest dany rekord.
- @DiscriminatorColumn - nazwa kolumny rozróżniającej, opcjonalna
- @DiscriminatorValue - wartość ten kolumny dla danej encji, również opcjonalna. Dla typu String jest to domyślnie nazwa klasy. Dla typów char i integer lepiej ją określić (domyślne wartości mogą być mało czytelne).
2. Tabela na konkretną klasę (InheritanceType.TABLE_PER_CLASS)
Każda konkretna klasa ma swoją tabelę. Wiele kolumn jest powielanych w tych tabelach. Mniej wydajne - wymusza łączenie po wielu tabelach w zapytaniach lub użycie union. Z zalet - nie trzeba ograniczać constraintów w bazie.
W encjach dziedziczących nie trzeba już dodawać metadanych związanych z dziedziczeniem. Dla tej strategii domyślny sposób generowania kluczy głównych nie zadziała (można go zmienić np na GenerationType.TABLE).
3. Tabela na subklasę (InheritanceType.JOINED)
Każda klasa ma tabelę i zawierającą kolumny dokładnie odpowiadające jej polom (z pominięciem pól dziedziczonych).
Nie tak wydajna jak SINGLE_TABLE, ale pozwala na constrainty. Bardziej znormalizowana niż TABLE_PER_CLASS.
Tabele w bazie dzielą IDki - tzn. wartości klucza głównego w każdej z tabel odpowiadają sobie. @PrimaryKeyJoinColumn jest opcjonalna, jeżeli nazwy kolumn z kluczem głównym w każdej tabeli są takie same.
W hierarchii dziedziczenia mogą znajdować się klasy nie będące encjami - oznaczamy je jako @MappedSuperclass. Mapowania można przesłonić poprzez @AttributeOverride. Niezadnotowane klasy są ignorowane przez PP.
Encja może być zmapowana na 2 tabele poprzez @SecondaryTable:
Jeżeli tych tabel jest więcej , używamy @SecondaryTables:
- 1:1 [->]
Pracownik ma adres. Tabela EMPLOYEE ma kolumnę ADDRESS_ID.
@JoinColumn: name (kolumna w tabeli EMPLOYEE, na której bazuje relacja), referencedColumnName (kolumna w tabeli ADDRESS, jeżeli jest inna niż klucz główny (musi być unique))
Adnotacja @JoinColumn jest opcjonalna (można ją pominąć, polegając na domyślnych wartościach). Jeżeli łączymy po kilku kolumnach, używamy @JoinColumns.
@OneToOne: targetEntity, cascade, fetch, optional, mappedBy(dla relacji dwukierunkowych), orphanRemoval(default false - określa, czy usunięcie Pracownika powinno sposodować usunięcie Adresu).
- 1:1 [<->]
Pracownik ma komputer. Komputer ma właściciela. COMPUTER ma kolumnę OWNER_ID.
W relacjach dwukierunkowych mamy do czynienia z encją nadrzędną i podrzędną. Encja podrzędna jest właścicielem relacji (owning side) i posiada klucz obcy do encji nadrzędnej (inverse). Strona inverse posiada adnotację z atrybutem mappedBy (wskazującym na pole w klasie właściciela definiujące relację). W tym przypadku właścicielem relacji jest komputer. (Pilnowanie, by relacja była poprawnie określona po obu stronach (computer.setOwner() i employee.setComputer()) jest zadaniem programisty.
- 1:N [->]
Pracownik ma telefony. Tabela PHONE ma kolumnę OWNER_ID. Model wygląda następująco:
Tym razem @JoinColumn umieszczona w klasie Employee wskazuje na kolumnę w tabeli Phone.
- N:1 [->]
Rejs ma przypisany statek. Tabela CRUISE ma kolumnę SHIP_ID.
- 1:N [<->]
Rejs ma rezerwacje. Tabela RESERVATION ma kolumnę CRUISE_ID.
Właścicielem relacji jest Rezerwacja. @ManyToOne nie może mieć atrybuty mappedBy.
- N:N [<->]
Rezerwacje są powiązane z klientami. Tabela łącząca RESERVATION_CUSTOMER ma kolumny RESERVATION_ID oraz CUSTOMER_ID.
Właścicielem jest Rezerwacja i to ona posiada adnotację @JoinTable.
- N:N [->]
Rezerwacje są powiązane z kabinami. Tabela łącząca CABIN_RESERVATION ma kolumny RESERVATION_ID i CABIN_ID.
Poza Collection i Set można też używać List i Map. Używając listy możemy dodać adnotację @OrderBy("name ASC, surname ASC"). Używając mapy określamy, która właściwość klasy ma być użyta jako klucz w mapiepoprzez @MapKey(name="number"). Bez tej adnotacji, jako klucz użyty zostanie klucz główny.
Polegając na domyślnych wartościach, można ograniczyć metadane relacji do jednej z adnotacji @[One/Many]To[One/Many], bez atrybutów.
Dla konkretnej kolumny lub relacji możemy określić fetchType jako EAGER lub LAZY. Pola z FetchType.LAZY zostaną pobrane z bazy dopiero przy próbie odwołania się do ich wartości (uwaga - fetchType to tylko wskazówka!). Jeżeli pierwsze odwołanie będzie miało miejsce w momencie, gdy encja jest odpięta od EM (detached), większość dostawców wyrzuci pewien rodzaj LazyInitializationException (choć specyfikacja tego nie określa). Rozwiązaniem jest znawigowanie do konkretnego pola, póki encja jest wciąż zarządzana lub jawne wyciągnięcie powiązanej encji w zapytaniu (join fetch).
Operacje kaskadowe
Adnotacje @[One/Many]To[One/Many] mają atrybut cascade, przyjmujący wartości: enum CascadeType{ALL, PERSIST, MERGE, REMOVE, REFRESH} (oraz DETACH od JEE6).
Przykład: @OneToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})
CascadeType.PERSIST - przy zapisie pracownika zapisany zostanie również jego adres.
CascadeType.MERGE - przy mergowaniu pracownika, zmergujemy również jego telefony. Jeżeli w międzyczasie dodaliśmy pracownikowi telefon, zostanie on wstawiony do bazy. Jeżeli usunęliśmy jeden z jego telefonów - nie zostanie usunięty (aby uzyskać taki efekt należałoby użyć orphanRemoval).
CascadeType.REMOVE - usuwając pracownika, usuniemy jego adres.
...
Hierarchia encji
Encje mogą tworzyć hierarchię. Przykładowo: Employee extends Customer extends Person. Możemy ją zmapować na 3 sposoby:
1. Tabela na hierarchię (InheritanceType.SINGLE_TABLE)
Jedna tabela zawiera wszystkie dane. Najprostsze, najwydajniejsze, z tym że musimy zrezygnować z constraintów not-null.
W tabeli musimy zdefiniować kolumnę discriminator - określającą, jakim typem osoby jest dany rekord.
- @DiscriminatorColumn - nazwa kolumny rozróżniającej, opcjonalna
- @DiscriminatorValue - wartość ten kolumny dla danej encji, również opcjonalna. Dla typu String jest to domyślnie nazwa klasy. Dla typów char i integer lepiej ją określić (domyślne wartości mogą być mało czytelne).
2. Tabela na konkretną klasę (InheritanceType.TABLE_PER_CLASS)
Każda konkretna klasa ma swoją tabelę. Wiele kolumn jest powielanych w tych tabelach. Mniej wydajne - wymusza łączenie po wielu tabelach w zapytaniach lub użycie union. Z zalet - nie trzeba ograniczać constraintów w bazie.
W encjach dziedziczących nie trzeba już dodawać metadanych związanych z dziedziczeniem. Dla tej strategii domyślny sposób generowania kluczy głównych nie zadziała (można go zmienić np na GenerationType.TABLE).
3. Tabela na subklasę (InheritanceType.JOINED)
Każda klasa ma tabelę i zawierającą kolumny dokładnie odpowiadające jej polom (z pominięciem pól dziedziczonych).
Nie tak wydajna jak SINGLE_TABLE, ale pozwala na constrainty. Bardziej znormalizowana niż TABLE_PER_CLASS.
Tabele w bazie dzielą IDki - tzn. wartości klucza głównego w każdej z tabel odpowiadają sobie. @PrimaryKeyJoinColumn jest opcjonalna, jeżeli nazwy kolumn z kluczem głównym w każdej tabeli są takie same.
W hierarchii dziedziczenia mogą znajdować się klasy nie będące encjami - oznaczamy je jako @MappedSuperclass. Mapowania można przesłonić poprzez @AttributeOverride. Niezadnotowane klasy są ignorowane przez PP.
Encja może być zmapowana na 2 tabele poprzez @SecondaryTable:
Jeżeli tych tabel jest więcej , używamy @SecondaryTables:
sobota, 13 października 2012
EJB 3.1 [2] - Persistence
Encje to klasy z adnotacją @javax.persistence.Entity (lub odpowiednikiem xml-owym). Mogą byc zarządzane (managed) lub odłączone (detached).
PersistenceContext to zbiór zarządanych encji. Zadządzaniem zajmuje się EntityManager. Śledzi zmiany wykonywane na encjach i utrwala je w bazie (to, kiedy zmiany zostaną zapisane do bazy zależy od flushMode).
PersistenceContext może być transakcyjny lub rozszerzony. Transakcyjny jest zamykany razem z końcem transakcji (a wszyskie encje wchodzące w jego skład zostają odłączone). Aby PC mógł być transakcyjny, musi być zarządzany przez serwer aplikacji (tzn. wstrzyknięty adnotacją lub xml-em). Przykład:
Przy rozszerzonym PC, encje są zarządzane również po zakończeniu transakcji (ale zmiany wprowadzone poza transakcją zostaną utrwalone w bazie dopiero w kolejnej transakcji). Rozszerzony PC może być utworzony i zarządzany ręcznie lub wstrzyknięty do StatefullSessionBean-a. Przykład kodu:
EntityManager mapuje zbiór klas na konkretną bazę danych. Ten zbiór klas to PersistenceUnit. PU można utworzyć zarówno w środowiskach Java EE(serwer aplikacji) jak i Java SE(standardowa aplikacja). PU (jeden lub więcej) definiuje się w pliku persistence.xml (jest to jedyny wymagany deskryptor w specyfikacji JavaPersistence).
Plik JAR lub katalog zawierajacy META-INF/persistence.xml nazywany jest rootem PU. Może nim być:
- plik EJB-JAR lub application client JAR
- katalog WEB-INF/classes w pliku WAR
- plik JAR w katalogu WEB-INF/lib pliku WAR lub w katalogu bibliotecznym pliku EAR
PersistenceUnit musi mieć nazwę (choć pusty string jest ok) i może być przypisany tylko do jednej bazy.
Definiująć PU można określić transaction-type: JTA(domyślne dla środowisk JEE) lub RESOURCE_LOCAL(domyślne dla środowisk SE). W przypadku RESOURE_LOCAL transakcje będą definiowane ręcznie, za pomocą EntityTransaction API. Definiujemy wówczas odpowiednio jta-data-source lub non-jta-data-source.
Zbiór klas wchodzących w skład PU zostaje określony następująco:
- wszystkie klasy z adnotacją @Entity w JARze (chyba że użyjemy )
- zbiór encji ze wskazanych jarów (poprzez)
- klasy zmapowane w META-INF/orm.xml (jeżeli taki plik istnieje)
- klasy zmapowane w XML-ach wskazanych przez
- klasy wymienione w elementach.
Przykład:
Uzyskiwanie EntityManagera
Możemy go wstrzyknąć poprzez adnotację @PersistenceContext(unitName="sampleUnit") lub utworzyć poprzez EntityManagerFactory.createEntityManager() (wówczas będzie miał zasięg rozszerzony - zasięg transakcji otrzymamy tylko poprzez wstrzyknięcie).Do StatefullSessionBeana można wstrzyknąć EM z zasięgiem rozszerzonym:
Utworzonego ręcznie EM należy ręcznie zamknąć (em.close()). Próba zamknięcie wstrzykniętego EM zakończy się IllegalStateException.
EntityManagerFactory można wstrzyknąć adnotacją @PersistenceUnit(unitName="sampleUnit"). Jeżeli jest ona JTA-Enabled, aby zsynchronizować wprowadzane zmiany z bazą, należy wywołać metodę EntityManager.joinTransaction().
EM wystawia kilka metod:
- em.find(X.class, xPrimaryKey) - ładuje obiekt z bazy. Jak wywołana w ramach transakcji, podpina obiekt pod EM (czyni go zarządzanym). Zwraca nulla, gdy obiekt nie istnieje.
- em.getReference(X.class, xPrimaryKey) - jak wyżej, z tym że zamiast nulla rzuca EntityNotFoundException.
- em.contains(xInstance) - sprawdza, czy obiekt jest zarządzany
- em.persist(xInstance) - wstawia obiekt do bazy (insert), niekoniecznie natychmiast (zależy to od flushMode)
- em.flush() - wymusza natychmiastowe zrzucenie zmian do bazy
- em.merge(xInstance) - przyjmuje odpiętą (detached) encję i zwraca jej podpięty odpowiednik. argument nie ulega zmianie.
- em.remove(xInstance) - usuwa encję z bazy
- em.refresh(xInstance) - nadpisuje encję danymi z bazy
- em.clear() - odpina wszystkie encje od PC
- em.unwrap(), em.getDelegate - zwraca obiekt dostawcy, kryjący się za EM. Pozwala na wykorzystanie rozszerzeń specyficznych dla konkretnego dostawcy.
Wiele z nich wypluje IllegalArgumentException, gdy parametr nie jest encją oraz TransactionRequiredException, jeżeli wywołane poza transakcją na PC o zasięgu transakcyjnym.
FlushMode
Domyślnie flushMode przyjmuje wartość AUTO - tzn. synchronizacja z bazą odbywa się tuż przed wykonaniem powiązanego zapytania oraz tuż przed zatwierdzeniem transakcji. Synchronizacja nie jest wykonywana przed find() ani getReference(). Można zmienić na FlushModeType.COMMIT - wówczas zmiany będą synchronizowane dopiero przed commitem.
Encja potrzebuje 2 metadanych @Entity i @Id. @Entity ma atrybut name - jest to nazwa, po której odwołujemy się do encji w JPQL i domyślnie jest to nazwa klasy. Poprzez @Id określamy klucz główny. PersistenceProvider zakłada, że wszystkie właściwości (properties) w klasie są mapowane na kolumny o tych samych nazwach i typie, a sama klasa na tabelę o tej samej nazwie.
Adnotację @Id można umieścić na polu lub na getterze. Wybór jest istotny i określa, w jaki sposób będą przebiegać odwołania do pól (Property/Field AccessType). Umieszczenie pozostałych adnotacji musi być spójne - albo na polach, albo na getterach/setterach.
Podstawowe adnotacje na encjach (oraz ich atrybuty):
- @Table: name(nazwa tabeli), catalog, schema, uniqueConstraints (przydatne, gdy chcemy wygenerować schemat bazy na podstawie encji)
- @Column: name(nazwa kolumny), unique, nullable, insertable, updatable(można ustawić na false dla kolumn wypełnianych triggerami), columnDefinition(dla określenia konkretnego DDL przy generowaniu schemaut), table(dla mapowań opierających się o kilka tabel), length, precision, scale
- @Transient - pole będzie ignorowane przy operacjach na bazie
- @Basic - domyślne mapowanie na polach. Możemy je dodać, jeżeli chcemy określić atrybuty fetch(default EAGER) i optional(default true). FetchType może zostać zmieniony na LAZY, jednak uwaga - jest to tylko wskazówka. Atrybut optional jest mapowany na nullable przy generacji schematu.
- @Lob - PErsistenceManager potraktuje takie pole jako blob (jeżeli jest typu byte[], Byte[] lub Serializable) lub clob (dla char[], Character[] lub String)
- @Temporal - dla pól typu @Date lub @Calendar. Określa typ pola jako TemporalType.DATE, TIME lub TIMESTAMP, np.: @Temporal(TemporalType.DATE)
- @Enumerated - EnumType.ORDINAL(default) lub STRING. Określa, czy enumy mają być zapisywane w bazie jako ich wartość numeryczna, czy jako wartość zwracana przez toString(). Przykład: @Enumerated(EnumType.STRING)
Klucze główne
Adnotacja @Id określa klucz główny dla encji. Klucz główny musi być prymitywem, wrapperem, Stringiem lub klasą PK składającą się z pól o takich typach. Wartości można generować i wstawiać ręcznie lub pozwolić, by zajął się tym PersistenceProvider - dodając adnotację @GeneratedValue obok @Id. Enum GenerationType ma wartości: TABLE, SEQUENCE, IDENTITY, AUTO(domyślna).
- Table:
Adnotacja @TableGenerator wskazuje tabelę przeznaczonado przechowywania aktualnych wartości "sekwencerów". W powyższym przykładzie, tabela ma kolumny PRIMARY_KEY_KOLUMN(nazwa klucza, dla którego generujemy wartości) oraz VALUE_COLUMN(aktuwalna wartość "sekwencera"). AtrybutpkColumnValue określa nazwę konkretnego klucza (wartość z kolumny PRIMARY_KEY_COLUMN).
- Sequence:
@SequenceGenerator wskazuje na rzeczywistego sekwencera (np. oraclowego).
- Identity - baza sama wie, jaki id nadać
Klasy PK
Klasa reprezentująca klucz główny musi być serializowalna, mieć bezargumentowy konstruktor i zaimplementowane equals i hashCode. Wówczas na klasie encji, obok @Entity dajemy adnotację @IdClass(SamplePKClass.class), a na polach encji odpowiadającym polom z tej klasy dajemy adnotację @Id.
Innuym rozwiązaniem jest stworzenie klasy zaadnotowanej jako @Embeddable. Jej pola mogą być zaadnotowane jako @Column. Wówczas we właściwej encji klucz główny będzie instancją takiej klasy. Zamiast @Id użyjemy na nim adnotacji @EmbeddedId:
W samej encji można przesłonić mapowania kolumn z klasy @Embedded poprzez adnotację @AttributeOverrides:
@Embedded
Niektóre pola encji mogą być złożonymi obiektami zaadnotowanymi jako @Embeddable. Ich mapowania można przesłonić poprzez @AttributeOverrides (opisaną wyżej). Jeżeli pole jest obiektem złożonym i nie ma adnotacji @Embeddable - zostanie potraktowany jak lob i zapisany jako strumień bajtów.
PersistenceContext to zbiór zarządanych encji. Zadządzaniem zajmuje się EntityManager. Śledzi zmiany wykonywane na encjach i utrwala je w bazie (to, kiedy zmiany zostaną zapisane do bazy zależy od flushMode).
PersistenceContext może być transakcyjny lub rozszerzony. Transakcyjny jest zamykany razem z końcem transakcji (a wszyskie encje wchodzące w jego skład zostają odłączone). Aby PC mógł być transakcyjny, musi być zarządzany przez serwer aplikacji (tzn. wstrzyknięty adnotacją lub xml-em). Przykład:
Przy rozszerzonym PC, encje są zarządzane również po zakończeniu transakcji (ale zmiany wprowadzone poza transakcją zostaną utrwalone w bazie dopiero w kolejnej transakcji). Rozszerzony PC może być utworzony i zarządzany ręcznie lub wstrzyknięty do StatefullSessionBean-a. Przykład kodu:
EntityManager mapuje zbiór klas na konkretną bazę danych. Ten zbiór klas to PersistenceUnit. PU można utworzyć zarówno w środowiskach Java EE(serwer aplikacji) jak i Java SE(standardowa aplikacja). PU (jeden lub więcej) definiuje się w pliku persistence.xml (jest to jedyny wymagany deskryptor w specyfikacji JavaPersistence).
Plik JAR lub katalog zawierajacy META-INF/persistence.xml nazywany jest rootem PU. Może nim być:
- plik EJB-JAR lub application client JAR
- katalog WEB-INF/classes w pliku WAR
- plik JAR w katalogu WEB-INF/lib pliku WAR lub w katalogu bibliotecznym pliku EAR
PersistenceUnit musi mieć nazwę (choć pusty string jest ok) i może być przypisany tylko do jednej bazy.
Definiująć PU można określić transaction-type: JTA(domyślne dla środowisk JEE) lub RESOURCE_LOCAL(domyślne dla środowisk SE). W przypadku RESOURE_LOCAL transakcje będą definiowane ręcznie, za pomocą EntityTransaction API. Definiujemy wówczas odpowiednio jta-data-source lub non-jta-data-source.
Zbiór klas wchodzących w skład PU zostaje określony następująco:
- wszystkie klasy z adnotacją @Entity w JARze (chyba że użyjemy
- zbiór encji ze wskazanych jarów (poprzez
- klasy zmapowane w META-INF/orm.xml (jeżeli taki plik istnieje)
- klasy zmapowane w XML-ach wskazanych przez
- klasy wymienione w elementach
Przykład:
Uzyskiwanie EntityManagera
Możemy go wstrzyknąć poprzez adnotację @PersistenceContext(unitName="sampleUnit") lub utworzyć poprzez EntityManagerFactory.createEntityManager() (wówczas będzie miał zasięg rozszerzony - zasięg transakcji otrzymamy tylko poprzez wstrzyknięcie).Do StatefullSessionBeana można wstrzyknąć EM z zasięgiem rozszerzonym:
Utworzonego ręcznie EM należy ręcznie zamknąć (em.close()). Próba zamknięcie wstrzykniętego EM zakończy się IllegalStateException.
EntityManagerFactory można wstrzyknąć adnotacją @PersistenceUnit(unitName="sampleUnit"). Jeżeli jest ona JTA-Enabled, aby zsynchronizować wprowadzane zmiany z bazą, należy wywołać metodę EntityManager.joinTransaction().
EM wystawia kilka metod:
- em.find(X.class, xPrimaryKey) - ładuje obiekt z bazy. Jak wywołana w ramach transakcji, podpina obiekt pod EM (czyni go zarządzanym). Zwraca nulla, gdy obiekt nie istnieje.
- em.getReference(X.class, xPrimaryKey) - jak wyżej, z tym że zamiast nulla rzuca EntityNotFoundException.
- em.contains(xInstance) - sprawdza, czy obiekt jest zarządzany
- em.persist(xInstance) - wstawia obiekt do bazy (insert), niekoniecznie natychmiast (zależy to od flushMode)
- em.flush() - wymusza natychmiastowe zrzucenie zmian do bazy
- em.merge(xInstance) - przyjmuje odpiętą (detached) encję i zwraca jej podpięty odpowiednik. argument nie ulega zmianie.
- em.remove(xInstance) - usuwa encję z bazy
- em.refresh(xInstance) - nadpisuje encję danymi z bazy
- em.clear() - odpina wszystkie encje od PC
- em.unwrap(), em.getDelegate - zwraca obiekt dostawcy, kryjący się za EM. Pozwala na wykorzystanie rozszerzeń specyficznych dla konkretnego dostawcy.
Wiele z nich wypluje IllegalArgumentException, gdy parametr nie jest encją oraz TransactionRequiredException, jeżeli wywołane poza transakcją na PC o zasięgu transakcyjnym.
FlushMode
Domyślnie flushMode przyjmuje wartość AUTO - tzn. synchronizacja z bazą odbywa się tuż przed wykonaniem powiązanego zapytania oraz tuż przed zatwierdzeniem transakcji. Synchronizacja nie jest wykonywana przed find() ani getReference(). Można zmienić na FlushModeType.COMMIT - wówczas zmiany będą synchronizowane dopiero przed commitem.
Encja potrzebuje 2 metadanych @Entity i @Id. @Entity ma atrybut name - jest to nazwa, po której odwołujemy się do encji w JPQL i domyślnie jest to nazwa klasy. Poprzez @Id określamy klucz główny. PersistenceProvider zakłada, że wszystkie właściwości (properties) w klasie są mapowane na kolumny o tych samych nazwach i typie, a sama klasa na tabelę o tej samej nazwie.
Adnotację @Id można umieścić na polu lub na getterze. Wybór jest istotny i określa, w jaki sposób będą przebiegać odwołania do pól (Property/Field AccessType). Umieszczenie pozostałych adnotacji musi być spójne - albo na polach, albo na getterach/setterach.
Podstawowe adnotacje na encjach (oraz ich atrybuty):
- @Table: name(nazwa tabeli), catalog, schema, uniqueConstraints (przydatne, gdy chcemy wygenerować schemat bazy na podstawie encji)
- @Column: name(nazwa kolumny), unique, nullable, insertable, updatable(można ustawić na false dla kolumn wypełnianych triggerami), columnDefinition(dla określenia konkretnego DDL przy generowaniu schemaut), table(dla mapowań opierających się o kilka tabel), length, precision, scale
- @Transient - pole będzie ignorowane przy operacjach na bazie
- @Basic - domyślne mapowanie na polach. Możemy je dodać, jeżeli chcemy określić atrybuty fetch(default EAGER) i optional(default true). FetchType może zostać zmieniony na LAZY, jednak uwaga - jest to tylko wskazówka. Atrybut optional jest mapowany na nullable przy generacji schematu.
- @Lob - PErsistenceManager potraktuje takie pole jako blob (jeżeli jest typu byte[], Byte[] lub Serializable) lub clob (dla char[], Character[] lub String)
- @Temporal - dla pól typu @Date lub @Calendar. Określa typ pola jako TemporalType.DATE, TIME lub TIMESTAMP, np.: @Temporal(TemporalType.DATE)
- @Enumerated - EnumType.ORDINAL(default) lub STRING. Określa, czy enumy mają być zapisywane w bazie jako ich wartość numeryczna, czy jako wartość zwracana przez toString(). Przykład: @Enumerated(EnumType.STRING)
Klucze główne
Adnotacja @Id określa klucz główny dla encji. Klucz główny musi być prymitywem, wrapperem, Stringiem lub klasą PK składającą się z pól o takich typach. Wartości można generować i wstawiać ręcznie lub pozwolić, by zajął się tym PersistenceProvider - dodając adnotację @GeneratedValue obok @Id. Enum GenerationType ma wartości: TABLE, SEQUENCE, IDENTITY, AUTO(domyślna).
- Table:
Adnotacja @TableGenerator wskazuje tabelę przeznaczonado przechowywania aktualnych wartości "sekwencerów". W powyższym przykładzie, tabela ma kolumny PRIMARY_KEY_KOLUMN(nazwa klucza, dla którego generujemy wartości) oraz VALUE_COLUMN(aktuwalna wartość "sekwencera"). AtrybutpkColumnValue określa nazwę konkretnego klucza (wartość z kolumny PRIMARY_KEY_COLUMN).
- Sequence:
@SequenceGenerator wskazuje na rzeczywistego sekwencera (np. oraclowego).
- Identity - baza sama wie, jaki id nadać
Klasy PK
Klasa reprezentująca klucz główny musi być serializowalna, mieć bezargumentowy konstruktor i zaimplementowane equals i hashCode. Wówczas na klasie encji, obok @Entity dajemy adnotację @IdClass(SamplePKClass.class), a na polach encji odpowiadającym polom z tej klasy dajemy adnotację @Id.
Innuym rozwiązaniem jest stworzenie klasy zaadnotowanej jako @Embeddable. Jej pola mogą być zaadnotowane jako @Column. Wówczas we właściwej encji klucz główny będzie instancją takiej klasy. Zamiast @Id użyjemy na nim adnotacji @EmbeddedId:
W samej encji można przesłonić mapowania kolumn z klasy @Embedded poprzez adnotację @AttributeOverrides:
@Embedded
Niektóre pola encji mogą być złożonymi obiektami zaadnotowanymi jako @Embeddable. Ich mapowania można przesłonić poprzez @AttributeOverrides (opisaną wyżej). Jeżeli pole jest obiektem złożonym i nie ma adnotacji @Embeddable - zostanie potraktowany jak lob i zapisany jako strumień bajtów.
niedziela, 26 sierpnia 2012
EJB 3.1 [1]
"Enterprise JavaBeans is a standard server-side component model for distributed business applications."
Rodzaje komponentów:
1. Stateless session beans [SLSB]
2. Stateful session beans [SFSB]
3. Singleton beans
4. Message-driven beans [MDB]
1. Stateless session beans [SLSB]
Podstawowy komponent. Bezstanowy. Dla każdego SLSB kontener tworzy pulę instancji. Do obługi żądania zostaje przydzielona losowa instancja z puli. Do obsługi kolejnego żądania znów zostaje przydzielona losowa instancja. Być może zupełnie inna. Dlatego nie przechowujemy danych w zmiennych beana. Efekt może wyglądać następująco:
- Upewniamy się, że ma bezargumentowy konstruktor.
- Dodajemy adnotację @javax.ejb.Stateless.
- Tworzymy interfejs zawierający metody, które chcemy wystawić i adnotujemy go jako @javax.ejb.Local albo @javax.ejb.Remote. Adnotacje te można też umieścić na klasie, wskazując odpowiedni interfejs, np: @Local(SampleLocalEJBInterface.class). Od EJB 3.1 można w ogóle nie określać interfejsu, adnotując klasę jako @LocalBean.
Można określić 2 interfejsy, Local i Remote (ale nie można utworzyć jednego interfejsu pełniącego obie funkcje).
Interfejs lokalny służy do wywołań w ramach tej samej JVM. Jest wydajnieszy od zdalnego. Argumenty są przekazywane przez referencję (w przypadku zdalnego - przez wartość).
- Zamiast adnotacji można wykorzystać deskryptor wdrożenia: plik ejb-jar.xml w katalogu META-INF tworzonego jara. Większość adnotacji można nadpisać w xml-u (choć nie wszystkie - np. nie można nadpisać typu beana). Plik ejb-jar.xml jest opcjonalny. Nie musi być pełny, tzn. może służyć jako uzupełnienie dla adnotacji. W przypadku, gdy ten sam element jest określony poprzez adnotację i w pliku xml, pierwszeństwo ma konfiguracja w xml'u.
Cykl życia SLSB składa się z 2 stanów: [Does not exist] i [Method-Ready pool]. Kontener tworząc instancję wywołuje Class.newInstance(), wstrzykuje wszystkie zależności, a następnie wywołuje metodę zaadnotowaną jako @PostConstruct. Przed usunięciem instancji wywołuje metodę @PreDestroy. Metody takie [lifecycle callback methods] mogą, ale nie muszą istnieć (maksymalnie jedna dla każdej adnotacji).
Dostęp do kontekstu można otrzymać wstrzykując obiekt SessionContext:
Interfejs ten rozszerza EJBContext, określając dodatkowe metody:
- getBusinessObject() - metoda zwraca referencję do beana, na którym została wywołana, tzn. odpowiednik wskaźnika this.
- getInvokedBusinessInterface() - zwraca interfejs, który został użyty podczas wywołania (local, remote, webservice).
- przestarzałe metody: getEJBLocalObject() i getEJBObject() - przy próbie wywołania wyrzucony zostanie wyjątek.
Sam interfejs javax.ejb.EJBContext ma następujące metody:
- lookup(String name) - wyszukuje obiekty w ENC (Enterprise Naming Context) EJB'ka
- getTimerService( )
- bezpieczeństwo: getCallerPrincipal(); isCallerInRole();
- transakcje: getUserTransaction(); getRollbackOnly();setRollbackOnly();
- przestarzałe metody: getCallerIdentity(); isCallerInRole(); getEnvironment(); getEJBHome(); getEJBLocalHome();
W ejb-jar.xml można określić wpisy środowiskowe:
a następnie wstrzyknąć je poprzez:
lub wyciągnąć z ENC:
Metody asynchroniczne
Zostały wprowadzone w EJB 3.1. Można je stosować w SLSB, SFSB i Singletonach. W przypadku, gdy wywoływana metoda może trochę potrwać, można wpierw zlecić jej wykonanie, a dopiero za jakiś czas pobrać wynik. Przykład:
Wywołanie:
2. Stateful session beans [SFSB]
SFSB przeznaczone są do obsługi konwersjacji (klasyczny przykład: koszyk z zakupami). Z konkretną sesją użytkownika powiązana jest jedna, konkretna instancja. Nie ma puli. Zamiast @Stateless używamy @Stateful. Dochodzi trzeci stan w cyklu życia: [Passive] oraz metody @PrePassivate i @PostActivate. Przejście do stanu [Passive] może nastąpić np. gdy kontener uzna, że użytkownik poszedł na lunch i nie ma co zajmować pamięci przechowując jego beana - wówczas instancja zostaje "zserializowana" i wyrzucona z pamięci. W stanach [Method-ready pool] i [Passive] może wystąpić timeout - instancja przechodzi wówczas do stanu [Does not exist]. Specyfikacja nie określa, czy przy timeoucie wywołana zostanie metoda @PreDestroy (zależy to od dostawcy).
W SFSB jedna z metod, kończąca konwersację, powinna mieć adnotację @Remove - po wywołaniu takiej metody instancja zostaje usunięta.
Z uwagi na pasywację i aktywację, stan beana może składać się jedynie z prymitywów, obiektów serializowalnych oraz następujących typów: [SessionContext, UserTransaction, javax.naming.Context, EntityManager, EntityManagerFactory, referencje do innych EJB, referencje do "managed resource factories" (np. javax.sql.DataSource)]. Referencje do obiektów z tej listy zostaną automatycznie odtworzone podczas aktywacji.
Pasywacja i aktywacja przypominają serializację/deserializację z jednym wyjątkiem: podczas deserializacji obiekty oznaczone jako @Transient otrzymują swoje domyślne wartości (np. 0 dla int, null dla referencji do obiektów); podczas aktywacji wartości te nie są określone (mogą być losowe).
Jeżeli podczas wykonywania metody beana sesyjnego wyrzucony zostanie wyjątek systemowy, bean zostaje usunięty z pamięci, bez wywoływania metody @PreDestroy. Wyjątki systemowe są to wszystkie wyjątki rozszerzające RuntimeException, które nie zostały oznaczone jako @ApplicationException. Wyjątki systemowe zostają opakowane przez kontener w EJBException.
3. Singleton beans
W EJB 3.1 określono kolejny komponent - singleton. Jak nazwa wskazuje, w ramach JVM istnieje maksymalnie jedna instancja takiego beana. Adnotacja: @javax.ejb.Singleton. Dodatkowo istnieje adnotacja @javax.ejb.Startup, pozwalająca wymusić utworzenie instancji beana zaraz po uruchomieniu aplikacji (a kod, który powinien się wówczas wykonać, można umieścić w metodzie @PostConstruct).
Wraz z Singletonem pojawił się problem współbieżności. (W przypadku SLSB i SFSB współbieżnością zajmuje się kontener. Żądania do SLSB dostają kolejne instancje z puli, a żądania do SFSB czekają w kolejce. Można uniemożliwić kolejkowanie żądań do SFSB poprzez adnotację @javax.ejb.ConcurrencyManagement - przy współbieżnym żądaniu zostanie wówczas wyrzucony wyjątek.) Adnotacja @javax.ejb.ConcurrencyManagement(javax.ejb.ConcurrencyManagementType.CONTAINER/BEAN/NOT_SUPPORTED) określa sposób zarządzania współbieżnością. Domyślną wartością jest CONTAINER. Każda metoda ma wówczas przypisany blokadę typu WRITE. Można to zmienić poprzez adnotację @javax.ejb.Lock(javax.ejb.LockType.READ) na poziomie klasy lub poszczególnych metod. Można też określić timeouty, by blokada nie była trzymana w nieskończoność: @javax.ejb.AccessTimeout(timeout=15,unit=java.util.concurrent.TimeUnit.SECONDS). W przypadku ConcurrencyManagementType.BEAN deweloperowi pozostaje własnoręczna zabawa takimi słowami kluczowymi jak synchronized i volatile.
4. Message-driven beans [MDB]
MDB to komponenty bezstanowe. Nie posiadają interfejsu. Nie są wywoływane bezpośrednio przez klienta - pełną rolę listenerów dla przychodzących komunikatów. Adnotacja @MessageDriven posiada atrybut activationConfig, określający konfigurację specyficzną dla systemu komunikatów, któregu używamy, np.:
Specyfikacja EJB określa zestaw stałych właściwości:
- acknowledgeMode - Auto-acknowledge oznacza, żę potwierdzenie otrzymania komunikatu zostanie wysłane natychmiast. Dups-ok-acknowledge pozwala kontenerowi odwlec wysłanie potwierdzenia w czasie. W związku z tym system komunikatów może uznać, że komunikat zaginął po drodze i wysłać go jeszcze raz. Dups-ok-acknowledge istnieje ze względów wydajnościowych, ale w rzeczywistości niewiele daje. AcknowledgeMode jest zazwyczaj ignorowany (chyba że mamy do czynienia z BMT (bean managed transactions) lub gdy transakcje nie są wspierane (NotSupported transaction attribute)).
- messageSelector - Pozwala filtrować otrzymywane komunikaty. Dopuszczalne wyrażenia to pozdbiór wyrażeń warunkowych SQL-92. Warunki odnoszą się do dodatkowych nagłówków komunikatu, ustawianych w następujący sposób: message.setStringProperty("MessageFormat","Version 3.4");
- subscriptionDurability - Durable oznacza, że gdy system padnie na jakiś czas, po ponownym uruchomieniu otrzyma wszystkie komunikaty, które zostały dostarczone w tym czasie. Dla not durable, komunikaty te przepadną. SubscriptionDurability nie ma większego znaczenia w przypadku javax.jms.Queue - z samej natury kolejki wiadomość będzie czekać na odebranie.
- destinationType
Cykl życia MDB nie różni się od cyklu życia SLSB. Posiada dwa stany: [Does Not Exist] i [Method-Ready Pool].
MessageDrivenContext rozszerza interfejs EJBContext, nie dodając nic od siebie. Można go wstrzyknąć:
Metody związane z interfejsami i bezpieczeństwem, wywołane z poziomu MDB wyrzucą wyjątek - w ramach MDB nie mamy zdefiniowanego użytkownika; MDB nie mają też lokalnych ani zdalnych interfejsów.
MDB zazwyczaj implementują interfejs javax.jms.MessageListener z metodą void onMessage(Message message). Dla systemów innych niż JMS interfejs może być inny.
JMS określa 2 rodzaje destynacji:
- Queue (model point-to-point) - Wiadomości zostają umieszczone w kolejce, a następnie z niej pobrane. Co najważniejsze - wiadomość zostanie pobrana dokładnie raz. Jeżeli kolejka ma więcej niż jednego klienta pobierającego wiadomości - nie jest określone, kto konkretną wiadomość dostanie.
- Topic (model publish/subscribe) - Wiadomości zostają wysyłane do konkretnych "grup tematycznych". Mogą zostać pobrane wielokrotnie, przez wielu klientów.
Przykład wysłania wiadomości do kolejki:
- Za pomocą ConnectionFactory tworzymy obiekt Connection.
- W ramach połączenia tworzymy sesję (connection.createSession(true, 0)).
- W ramach sesji tworzymy obiekty MessageProducer i ObjectMessage (session.createProducer(queue); session.createObjectMessage())
- Wypełniamy wiadomość i wysyłamy (producer.send(message))
- Zamykamy połączenie (connection.close())
Argumenty metody createSession(boolean transacted, int acknowledgeMode) są ignorowane - zaleca się jednak wartości true i 0.
Typy komunikatów: TextMessage, MapMessage, ObjectMessage, StreamMessage, BytesMessage.
Poza JMS-based MDB istnieją jeszcze Connector-Based MDB - np. do komunikacji poprzez emaile lub SOAP.
Rodzaje komponentów:
1. Stateless session beans [SLSB]
2. Stateful session beans [SFSB]
3. Singleton beans
4. Message-driven beans [MDB]
1. Stateless session beans [SLSB]
Podstawowy komponent. Bezstanowy. Dla każdego SLSB kontener tworzy pulę instancji. Do obługi żądania zostaje przydzielona losowa instancja z puli. Do obsługi kolejnego żądania znów zostaje przydzielona losowa instancja. Być może zupełnie inna. Dlatego nie przechowujemy danych w zmiennych beana. Efekt może wyglądać następująco:
Mike: Hi, my name is Mike.Jak ze zwykłej klasy zrobić SLSB:
SLSB: Hello, Mike.
Mike: Do you know my name?
SLSB: Yes, your name is Jason.
Mike: What? That's wrong! I'm going!
SLSB: OK, later, Dave.
- Upewniamy się, że ma bezargumentowy konstruktor.
- Dodajemy adnotację @javax.ejb.Stateless.
- Tworzymy interfejs zawierający metody, które chcemy wystawić i adnotujemy go jako @javax.ejb.Local albo @javax.ejb.Remote. Adnotacje te można też umieścić na klasie, wskazując odpowiedni interfejs, np: @Local(SampleLocalEJBInterface.class). Od EJB 3.1 można w ogóle nie określać interfejsu, adnotując klasę jako @LocalBean.
Można określić 2 interfejsy, Local i Remote (ale nie można utworzyć jednego interfejsu pełniącego obie funkcje).
Interfejs lokalny służy do wywołań w ramach tej samej JVM. Jest wydajnieszy od zdalnego. Argumenty są przekazywane przez referencję (w przypadku zdalnego - przez wartość).
- Zamiast adnotacji można wykorzystać deskryptor wdrożenia: plik ejb-jar.xml w katalogu META-INF tworzonego jara. Większość adnotacji można nadpisać w xml-u (choć nie wszystkie - np. nie można nadpisać typu beana). Plik ejb-jar.xml jest opcjonalny. Nie musi być pełny, tzn. może służyć jako uzupełnienie dla adnotacji. W przypadku, gdy ten sam element jest określony poprzez adnotację i w pliku xml, pierwszeństwo ma konfiguracja w xml'u.
Cykl życia SLSB składa się z 2 stanów: [Does not exist] i [Method-Ready pool]. Kontener tworząc instancję wywołuje Class.newInstance(), wstrzykuje wszystkie zależności, a następnie wywołuje metodę zaadnotowaną jako @PostConstruct. Przed usunięciem instancji wywołuje metodę @PreDestroy. Metody takie [lifecycle callback methods] mogą, ale nie muszą istnieć (maksymalnie jedna dla każdej adnotacji).
Dostęp do kontekstu można otrzymać wstrzykując obiekt SessionContext:
Interfejs ten rozszerza EJBContext, określając dodatkowe metody:
- getBusinessObject() - metoda zwraca referencję do beana, na którym została wywołana, tzn. odpowiednik wskaźnika this.
- getInvokedBusinessInterface() - zwraca interfejs, który został użyty podczas wywołania (local, remote, webservice).
- przestarzałe metody: getEJBLocalObject() i getEJBObject() - przy próbie wywołania wyrzucony zostanie wyjątek.
Sam interfejs javax.ejb.EJBContext ma następujące metody:
- lookup(String name) - wyszukuje obiekty w ENC (Enterprise Naming Context) EJB'ka
- getTimerService( )
- bezpieczeństwo: getCallerPrincipal(); isCallerInRole();
- transakcje: getUserTransaction(); getRollbackOnly();setRollbackOnly();
- przestarzałe metody: getCallerIdentity(); isCallerInRole(); getEnvironment(); getEJBHome(); getEJBLocalHome();
W ejb-jar.xml można określić wpisy środowiskowe:
a następnie wstrzyknąć je poprzez:
lub wyciągnąć z ENC:
Metody asynchroniczne
Zostały wprowadzone w EJB 3.1. Można je stosować w SLSB, SFSB i Singletonach. W przypadku, gdy wywoływana metoda może trochę potrwać, można wpierw zlecić jej wykonanie, a dopiero za jakiś czas pobrać wynik. Przykład:
Wywołanie:
2. Stateful session beans [SFSB]
SFSB przeznaczone są do obsługi konwersjacji (klasyczny przykład: koszyk z zakupami). Z konkretną sesją użytkownika powiązana jest jedna, konkretna instancja. Nie ma puli. Zamiast @Stateless używamy @Stateful. Dochodzi trzeci stan w cyklu życia: [Passive] oraz metody @PrePassivate i @PostActivate. Przejście do stanu [Passive] może nastąpić np. gdy kontener uzna, że użytkownik poszedł na lunch i nie ma co zajmować pamięci przechowując jego beana - wówczas instancja zostaje "zserializowana" i wyrzucona z pamięci. W stanach [Method-ready pool] i [Passive] może wystąpić timeout - instancja przechodzi wówczas do stanu [Does not exist]. Specyfikacja nie określa, czy przy timeoucie wywołana zostanie metoda @PreDestroy (zależy to od dostawcy).
W SFSB jedna z metod, kończąca konwersację, powinna mieć adnotację @Remove - po wywołaniu takiej metody instancja zostaje usunięta.
Z uwagi na pasywację i aktywację, stan beana może składać się jedynie z prymitywów, obiektów serializowalnych oraz następujących typów: [SessionContext, UserTransaction, javax.naming.Context, EntityManager, EntityManagerFactory, referencje do innych EJB, referencje do "managed resource factories" (np. javax.sql.DataSource)]. Referencje do obiektów z tej listy zostaną automatycznie odtworzone podczas aktywacji.
Pasywacja i aktywacja przypominają serializację/deserializację z jednym wyjątkiem: podczas deserializacji obiekty oznaczone jako @Transient otrzymują swoje domyślne wartości (np. 0 dla int, null dla referencji do obiektów); podczas aktywacji wartości te nie są określone (mogą być losowe).
Jeżeli podczas wykonywania metody beana sesyjnego wyrzucony zostanie wyjątek systemowy, bean zostaje usunięty z pamięci, bez wywoływania metody @PreDestroy. Wyjątki systemowe są to wszystkie wyjątki rozszerzające RuntimeException, które nie zostały oznaczone jako @ApplicationException. Wyjątki systemowe zostają opakowane przez kontener w EJBException.
3. Singleton beans
W EJB 3.1 określono kolejny komponent - singleton. Jak nazwa wskazuje, w ramach JVM istnieje maksymalnie jedna instancja takiego beana. Adnotacja: @javax.ejb.Singleton. Dodatkowo istnieje adnotacja @javax.ejb.Startup, pozwalająca wymusić utworzenie instancji beana zaraz po uruchomieniu aplikacji (a kod, który powinien się wówczas wykonać, można umieścić w metodzie @PostConstruct).
Wraz z Singletonem pojawił się problem współbieżności. (W przypadku SLSB i SFSB współbieżnością zajmuje się kontener. Żądania do SLSB dostają kolejne instancje z puli, a żądania do SFSB czekają w kolejce. Można uniemożliwić kolejkowanie żądań do SFSB poprzez adnotację @javax.ejb.ConcurrencyManagement - przy współbieżnym żądaniu zostanie wówczas wyrzucony wyjątek.) Adnotacja @javax.ejb.ConcurrencyManagement(javax.ejb.ConcurrencyManagementType.CONTAINER/BEAN/NOT_SUPPORTED) określa sposób zarządzania współbieżnością. Domyślną wartością jest CONTAINER. Każda metoda ma wówczas przypisany blokadę typu WRITE. Można to zmienić poprzez adnotację @javax.ejb.Lock(javax.ejb.LockType.READ) na poziomie klasy lub poszczególnych metod. Można też określić timeouty, by blokada nie była trzymana w nieskończoność: @javax.ejb.AccessTimeout(timeout=15,unit=java.util.concurrent.TimeUnit.SECONDS). W przypadku ConcurrencyManagementType.BEAN deweloperowi pozostaje własnoręczna zabawa takimi słowami kluczowymi jak synchronized i volatile.
4. Message-driven beans [MDB]
MDB to komponenty bezstanowe. Nie posiadają interfejsu. Nie są wywoływane bezpośrednio przez klienta - pełną rolę listenerów dla przychodzących komunikatów. Adnotacja @MessageDriven posiada atrybut activationConfig, określający konfigurację specyficzną dla systemu komunikatów, któregu używamy, np.:
Specyfikacja EJB określa zestaw stałych właściwości:
- acknowledgeMode - Auto-acknowledge oznacza, żę potwierdzenie otrzymania komunikatu zostanie wysłane natychmiast. Dups-ok-acknowledge pozwala kontenerowi odwlec wysłanie potwierdzenia w czasie. W związku z tym system komunikatów może uznać, że komunikat zaginął po drodze i wysłać go jeszcze raz. Dups-ok-acknowledge istnieje ze względów wydajnościowych, ale w rzeczywistości niewiele daje. AcknowledgeMode jest zazwyczaj ignorowany (chyba że mamy do czynienia z BMT (bean managed transactions) lub gdy transakcje nie są wspierane (NotSupported transaction attribute)).
- messageSelector - Pozwala filtrować otrzymywane komunikaty. Dopuszczalne wyrażenia to pozdbiór wyrażeń warunkowych SQL-92. Warunki odnoszą się do dodatkowych nagłówków komunikatu, ustawianych w następujący sposób: message.setStringProperty("MessageFormat","Version 3.4");
- subscriptionDurability - Durable oznacza, że gdy system padnie na jakiś czas, po ponownym uruchomieniu otrzyma wszystkie komunikaty, które zostały dostarczone w tym czasie. Dla not durable, komunikaty te przepadną. SubscriptionDurability nie ma większego znaczenia w przypadku javax.jms.Queue - z samej natury kolejki wiadomość będzie czekać na odebranie.
- destinationType
Cykl życia MDB nie różni się od cyklu życia SLSB. Posiada dwa stany: [Does Not Exist] i [Method-Ready Pool].
MessageDrivenContext rozszerza interfejs EJBContext, nie dodając nic od siebie. Można go wstrzyknąć:
Metody związane z interfejsami i bezpieczeństwem, wywołane z poziomu MDB wyrzucą wyjątek - w ramach MDB nie mamy zdefiniowanego użytkownika; MDB nie mają też lokalnych ani zdalnych interfejsów.
MDB zazwyczaj implementują interfejs javax.jms.MessageListener z metodą void onMessage(Message message). Dla systemów innych niż JMS interfejs może być inny.
JMS określa 2 rodzaje destynacji:
- Queue (model point-to-point) - Wiadomości zostają umieszczone w kolejce, a następnie z niej pobrane. Co najważniejsze - wiadomość zostanie pobrana dokładnie raz. Jeżeli kolejka ma więcej niż jednego klienta pobierającego wiadomości - nie jest określone, kto konkretną wiadomość dostanie.
- Topic (model publish/subscribe) - Wiadomości zostają wysyłane do konkretnych "grup tematycznych". Mogą zostać pobrane wielokrotnie, przez wielu klientów.
Przykład wysłania wiadomości do kolejki:
- Za pomocą ConnectionFactory tworzymy obiekt Connection.
- W ramach połączenia tworzymy sesję (connection.createSession(true, 0)).
- W ramach sesji tworzymy obiekty MessageProducer i ObjectMessage (session.createProducer(queue); session.createObjectMessage())
- Wypełniamy wiadomość i wysyłamy (producer.send(message))
- Zamykamy połączenie (connection.close())
Argumenty metody createSession(boolean transacted, int acknowledgeMode) są ignorowane - zaleca się jednak wartości true i 0.
Typy komunikatów: TextMessage, MapMessage, ObjectMessage, StreamMessage, BytesMessage.
Poza JMS-based MDB istnieją jeszcze Connector-Based MDB - np. do komunikacji poprzez emaile lub SOAP.
Subskrybuj:
Posty (Atom)