Zarządzaniee encjami
Transkrypt
Zarządzaniee encjami
Zarządzanie encjami 1. Komponenty i JBoss ● komponenty EJB, ● program kliencki. 2. Usługa EntityManager ● encje zarządzane i niezarządzane, ● kontekst transakcji, ● podstawowe usługi, ● transakcje poza JEE. 1 Komponenty i JBoss Struktura programu: 1. Serwer (titan.jar): ● komponent encyjny: Cabin ● deskryptor utrwalania: persistance.xml ● komponent sesyjny: TravelAgent 2. Klient: ● biblioteki, ● konfguracja JNDI, ● program korzystający z komponentów. 2 Komponent encyjny: Cabin package com.titan.domain; import import import import javax.persistence.Entity; javax.persistence.Table; javax.persistence.Column; javax.persistence.Id; @Entity @Table(name = "CABIN") public class Cabin implements java.io.Serializable { private int id; private String name; private int deckLevel; private int shipId; private int bedCount; 3 Komponent encyjny: Cabin @Id @Column(name = "ID") public int getId() { return id; } public void setId(int pk) { id = pk; } @Column(name = "NAME") public String getName() { return name; } public void setName(String str) { name = str; } @Column(name = "DECK_LEVEL") public int getDeckLevel() { return deckLevel; } public void setDeckLevel(int level) { deckLevel = level;} @Column(name = "SHIP_ID") public int getShipId() { return shipId; } public void setShipId(int sid) { shipId = sid; } } @Column(name = "BED_COUNT") public int getBedCount() { return bedCount; } public void setBedCount(int bed) { bedCount = bed; } 4 Deskryptor utwalania Plik persistence.xml znajduje się w podkatalogu META-INF <?xml version="1.0" encoding="UTF-8"?> <persistence> <persistence-unit name="titan"> <jta-data-source>java:/DefaultDS</jta-data-source> <properties> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> </properties> </persistence-unit> </persistence> 5 Komponent sesyjny: TravelAgent package com.titan.travelagent; import javax.ejb.Remote; import com.titan.domain.Cabin; @Remote public interface TravelAgentRemote { public void createCabin(Cabin cabin); public Cabin findCabin(int pKey); } public void updateCabin(Cabin cabin); 6 Komponent sesyjny: TravelAgent package com.titan.travelagent; import import import import javax.ejb.Stateless; javax.persistence.EntityManager; javax.persistence.PersistenceContext; com.titan.domain.Cabin; @Stateless public class TravelAgentBean implements TravelAgentRemote { @PersistenceContext(unitName = "titan") private EntityManager manager; public void createCabin(Cabin cabin) { manager.persist(cabin); } public Cabin findCabin(int pKey) { return manager.find(Cabin.class, pKey); } public void updateCabin(Cabin cabin) { manager.merge(cabin); } } 7 Biblioteki dla klienta Do prawidłowego działania niezbędne są biblioteki (narzędzia) pozwalające korzystać z usług kontenera, a w przypadku oprogramowania klienckiego ze zdalnych komponentów. Biblioteki są udostępniane przez producenta kontenera EJB. W przypadku serwera JBoss należy użyć z następujących plików: ● ejb3-persistence.jar ● jboss-aop-jdk50-client.jar ● jboss-aspect-jdk50-client.jar ● jboss-ejb3-client.jar ● jboss-ejb3x.jar ● jbossall-client.jar które można znaleźć w podkatalogu client/ serwera. 8 Konfguracja JNDI u klienta Konfguracja JNDI zapisana jest w pliku jndi.properties. Aby zachować takie ustawienia dla wszystkich programów korzystających z JNDI plik należy umieścić na ścieżce klas. java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=127.0.0.1 9 Program klienta package com.titan.clients; import java.io.IOException; import java.util.Properties; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import com.titan.domain.Cabin; import com.titan.travelagent.TravelAgentRemote; public class Client { static{ Properties p = new Properties(); try { p.load(Client.class.getResourceAsStream( } "jndi.properties")); } catch (IOException e) { e.printStackTrace(); } System.getProperties().putAll(p); 10 Program kliencki public static Context getInitialContext() throws NamingException { return new InitialContext(); } public static void main(String[] args) { try { Context jndiContext = getInitialContext(); Object ref = jndiContext.lookup("TravelAgentBean/remote"); TravelAgentRemote dao = (TravelAgentRemote) ref; Cabin c = dao.findCabin(1); if (c==null){ System.out.println("new cabin"); c = new Cabin(); 11 Program kliencki c.setId(1); c.setName("Master Suite"); c.setDeckLevel(1); c.setShipId(1); c.setBedCount(3); dao.createCabin(c); }else{ System.out.println("present cabin"); } System.out.println(c.getName()); System.out.println(c.getDeckLevel()); System.out.println(c.getShipId()); System.out.println(c.getBedCount()); } } } catch (javax.naming.NamingException ne) { ne.printStackTrace(); } 12 build.xml <?xml version="1.0"?> <project name="wzorce-03" default="ejbjar" basedir="."> <property environment="env"/> <property name="src.dir" value="${basedir}/src"/> <property name="lib.dir" value="${basedir}/lib"/> <property name="classes.dir" value="${basedir}/bin"/> <property name="build.dir" value="${basedir}/build"/> <target name="ejbjar"> <jar jarfile="${build.dir}/titan.jar"> <fileset dir="${classes.dir}"> <include name="com/titan/domain/*.class"/> <include name="com/titan/travelagent/*.class"/> </fileset> <fileset dir="${src.dir}/"> <include name="META-INF/persistence.xml"/> </fileset> </jar> </target> 13 build.xml <target name="run.client"> <java classname="com.titan.clients.Client" fork="yes" dir="."> <classpath> <fileset dir="${classes.dir}"> <include name="${lib.dir}/*.jar"/> </fileset> </classpath> </java> </target> </project> 14 Usługa EntityManager Menadżer encji udostępnia metody pozwalające wykonywać podstawowe operacje na tych obiektach. Do operacji tych należą: ● utrwalanie, ● wyszukiwanie, ● scalanie, ● usuwanie, ● synchronizacja, ● blokowanie dostępu. źródło: http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html 15 Utrwalanie Utrwalenie POJO dokonuje się za pomocą metody void persist(Object). W jej wyniku menadżer umieszcza w kolejce odpowiednie żądanie zapisu obiektu w bazie danych. Moment rzeczywistego zapisu zależy od kontenera i innych okoliczności (transakcje). Ponadto metoda ta dołącza POJO do zbioru zarządzanych encji. Typowe wyjątki: IllegalArgumentException – argument nie jest encją, EntityExistsException – encja już istnieje, TransactionRequiredException – wywołanie poza kontekstem transakcji. 16 Encje zarządzane i niezarządzane Po utworzeniu obiektu Cabin za pomocą konstruktora jest on zwykłym POJO. Zmiany atrybutów nie mają żadnego wpływu na zawartość bazy danych. Encja jest niezarządzana (odłączona). Dopiero wywołanie metody EntityManager.persist() powoduje związanie stanu obiektu z bazą danych. Od tej chwili encja jest zarządzana (dołączona do zbioru zarządzanych encji) w ramach określonego kontekstu transakcyjnego. Cabin c = new Cabin(); ... // manager.persist(c); // ... // return c; // encja niezarządzana utrwalenie i początek zarządzania encja zarządzana zwykle koniec zarządzania 17 Odnajdywanie – klucz główny Do uzyskania referencji do encji na podstawie klucza głównego służą metody: <T> T find(Class<T>, Object) – zwraca referencję do encji lub null, <T> T getReference(Class<T>, Object) – zwraca referencję do encji, której stan może być załadowany dopiero w momencie odwołania się do niego (lazy fetched). W przypadku nieodnalezienia encji zwracany jest wyjątek EntityNotFoundException. Ze względu na sposób działania metody, wyjątek może zostać zwrócony dopiero w momencie odwołania się do stanu encji. 18 Odnajdywanie – język QL Do uzyskania referencji można skorzystać także z zapytań wyrażonych w języku QL (Query Language). Query query = manager.createQuery("from Cabin c where id=2"); Cabin c = (Cabin)query.getSingleResult( ); Wszystkie encje zwracane przez find(), getReference(), czy też zapytania QL są zarządzane dopóki kontekst zarządzania, w którym zostały odczytane jest aktywny. 19 Kontekst transakcji i zarządzanie Adnotacja @TransactionAttribute(TransactionAttributeType.REQUIRED) powoduje, że metoda someMethod() zostanie wykonana jako transakcja. @PersistenceContext(unitName = "titan") EntityManager entityManager; @TransactionAttribute(TransactionAttributeType.REQUIRED) // kontekst transakcji public Cabin someMethod() { Cabin c = entityManager.find(Cabin.class, 1); // uzyskanie referencji do encji // zarządzanej c.setName("new name"); return c; } // // // // zmiana automatycznie zapisana w bazie danych koniec transakcji – encja niezarządzana wszelkie zmiany na zwróconej referencji nie będą odwzorowane w bazie danych, aż do kolejnego wywołania odpowiedniej metody 20 Scalanie encji Encje odłączone można ponownie związać z utrwalonym stanem w bazie danych korzystając z metody <T> T merge(T). @TransactionAttribute(TransactionAttributeType.REQUIRED) public Cabin update(Cabin c) { // c – encja (odłączona lub dołączona) Cabin copy = manager.merge(c); // copy - encja zarządzana (przyłączona) z atrybutami // skopiowanymi z c // c - encja odłączona copy.setName("name1"); // nazwa zmieniona w obiekcie i bazie c.setName("name2"); // nazwa zmieniona tylko w obiekcie } 21 Usuwanie encji Usuwanie pojedynczej encji można zrealizować za pomocą operacji void remove(Object). Powoduje ono dodanie do kolejki odpowiedniej operacji DELETE wykonywanej na bazie danych. @TransactionAttribute(TransactionAttributeType.REQUIRED) public Cabin removeCabin(int key) { Cabin cabin = manager.find(Cabin.class, key); manager.remove(cabin); return cabin; } Operację remove() można „odwrócić” wywołując metodę persist() z tą samą encją w roli argumentu. 22 Odświeżanie encji Jeżeli chcemy odświeżyć stan zarządzanej encji na podstawie informacji w bazie danych używamy metody void refresh(Object). @TransactionAttribute(TransactionAttributeType.REQUIRED) public void someMethod(int id) { Cabin cabin = manager.find(Cabin.class, id); manager.refresh(cabin); } W przypadku braku encji w bazie lub wśród obiektów zarządzanych zwracany jest IllegalArgumentException. 23 Inne operacje Interfejs EntityManager określa także inne metody. Najczęściej używane to: ● boolean contains(Object) – sprawdza, czy encja jest zarządzana, ● void clear() - odłącza wszystkie zarządzane encje, ● void flush() - synchronizuje bazę danych ze stanem encji. Synchronizacja odbywa się w zależności od parametru ustawionego metodą: setFlushMode(FlushModeType). Domyślny typ to AUTO odpowiadający synchronizacji na poziomie zapytań. Typ COMMIT określa synchronizację na poziomie transakcji. ● EntityTransaction getTransaction() - zwraca obiekt do zarządzania transakcjami na poziomie zasobów. 24 Transakcje Transakcje w ramach contenera JEE są zarządzane poprzez JTA (Java Transaction Api). W środowisku niezgodnym z JEE (np. w programach klienckich) transakcjami można zarządzać poprzez interfejs EntityTransaction. public interface EntityTransaction { public void begin( ); public void commit( ); public void rollback( ); public boolean isActive( ); } 25 Transakcje ... // uzyskanie interfejsu EntityManagerFactory EntityManagerFactory factory = Persistence .createEntityManagerFactory("titan"); // uzyskanie kontekstu utrwalania EntityManager manager = factory.createEntityManager(); ... // uzyskanie interfejsu do zarządzania transakcjami EntityTransaction transaction = (EntityTransaction) manager .getTransaction(); transaction.begin(); // początek transakcji ... manager.persist(cabin_1); ... transaction.commit(); // koniec transakcji 26 Podsumowanie Usługa EntityManager umożliwia zarządzanie komponentami encyjnymi realizując podstawowe operacje, takie jak: utrwalanie, synchronizacja, usuwanie. Ponadto EntityManager udostępnia interfejs transakcyjny. Usługa EntityManager jest dostępna zarówno dla komponentów działających w ramach kontenera EJB jak również dla programów klienckich. 27