real life persistence
DESCRIPTION
Mit JPA 2.1 wurde in Java EE 7 ein weiterer Schritt in Richtung Provider-Unabhängigkeit gegangen. Auch wenn damit weitere Möglichkeiten des Einsatzes mit JPA geschaffen wurden, gibt es in der Praxis weiterhin viele Dinge zu beachten.In der Session werden neben klassischen Architekturansetzen zum Einsatz von JPA auch weitergehende Herausforderungen diskutiert. Wie kann JPA im Zusammenhang mit den neuen JSRs für Batch-Verarbeitung und den Concurrency Utilities beleuchtet, die erweitere Möglichkeiten im Bereich Asynchronität bieten?TRANSCRIPT
Arne Limburg | open knowledge GmbH
Real Life Persistence
Arne LimburgHead of Architecture and Technologyopen knowledge GmbH
@ArneLimburg@_openknowledge
www.openknowledge.de
Meine Person
Beispiel
N + 1 SELECTS
Lazy Loading
Lazy Loading
Problemstellung:
Wann werden die Daten aus der Datenbank geladen?
Beispiel:
Customer customer = entityManager.find(Customer.class, id);Address address = customer.getAddress();String street = address.getStreet();
//Wann wird die Adresse aus der Datenbank geladen?
Lazy Loading
Möglichkeit 1: Wenn ein User geladen wird, werden alle angehängten Objekte mitgeladen!
Problem: Wenn alle Objekte zusammenhängen, wird bei jedem Select der gesamte Datenbank-Inhalt geladen!
Lazy Loading
Möglichkeit 2: Anhängende Objekte werden nie mitgeladen!
Problem:
Customer customer = entityManager.find(Customer.class, id);Address address = customer.getAddress();String street = address.getStreet();//führt zu LazyInitializationException
Lazy Loading
Möglichkeit 3: Anhängende Objekte werden nachgeladen, wenn sie gebraucht werden!Vorgehen von JPA bei geöffnetem
EntityManagerBei geschlossenem EntityManager führt Zugriff
auf nicht geladene Entitäten zu einem Fehler
Lazy Loading
Beeinflussungsmöglichkeiten des Nachladens
>Mapping
>Query
>Manuell (Test über PersistenceUnitUtil)
>Entity Graph Neu ab JPA 2.1
Lazy Loading
Nachladen beeinflussen über Mapping
>fetch-Attribut an
>@ManyToOne (default EAGER)
>@OneToOne (default EAGER)
>@OneToMany (default LAZY)
>@ManyToMany (default LAZY)
Lazy Loading
Nachladen beeinflussen über Query
>Angabe von join fetch in JPQL
>Angabe von root.fetch(…)in Criteria
Lazy Loading
Manuell
>Zugriff auf Objekt führt zu Nachladen
>Überprüfen des Ladezustandes mit PersistenceUnitUtil>Über EntityManagerFactory
>Zusätzlich Möglichkeit, die Id zu erhalten
Neu seit JPA 2
Entity Graph
>Spezifikation des Fetch-Verhaltens
>Find-Operationen (via Map)
>Queries (via setHint)
>Angabe über
>javax.persistence.fetchgraph Alle nicht enthaltenen Attribute lazy
>javax.persistence.loadgraph Alle nicht enthaltenen Attribute default
Neu seit JPA 2.1
@NamedEntityGraph( name = "User.fetchRoles", attributeNodes = { @NamedAttributeNode("name"), @NamedAttributeNode(value = "roles", subgraph = "role") }, subgraphs = { @NamedSubgraph(name = "role", attributeNodes = …)}) public class User {
Entity Graph
EntityGraph<User> userGraph = entityManager .createEntityGraph(User.class);userGraph.addAttributeNode(User_.name);Subgraph<Role> role = userGraph.addSubgraph(User_.roles);role.addAttributeNode(…);
Entity Graph
Proxy vs. Enhancement
Proxy vs. Enhancement
Aufpassen bei
>equals>Subclassing
>Serialisierung
>Lazy to-one-Beziehungen
New
Managed Detached
Removed
entityManager.persistentityManager.remove
entityManager.detachentityManager.clearentityManager.close
entityManager.mergeentityManager.findquery.getResultList()query.getSingleResult()
Entity-Lebenszyklus
entityManager.merge
New
Managed
Removed
entityManager.persistentityManager.merge
entityManager.remove
entityManager.detachentityManager.close
entityManager.mergeentityManager.findquery.getResultList()query.getSingleResult()
Entity-Lebenszyklus
Detached
Achtung:
Lazy-Initialization nicht im Zustand „detached“
Wann wird der EntityManagereigentlich geschlossen?
Fragestellungen
>EntityManager
>Container- vs. Application-Managed
>Transactional vs. Extended
>Transaction
>JTA vs. Resource Local
>Synchronized vs. Unsynchronized
>Datasource
>JNDI vs. local
>JTA vs. Non-JTA
Application-Managed
>Erzeugung via emf.createEntityManager(…)
>Transaktionsbehandlung (Manuell)
>JTA em.joinTransaction()>RESOURCE_LOCAL em.getTransaction().begin() em.getTransaction().commit()
>Scope EXTENDED (Manuelle Verwaltung)
Application-Managed
>Erzeugung via emf.createEntityManager(…)
>Transaktionsbehandlung (Manuell)
>JTA em.joinTransaction()>RESOURCE_LOCAL em.getTransaction().begin() em.getTransaction().commit()
>Scope EXTENDED (Manuelle Verwaltung)
Nachteil:
Keine PerstenceContext-Propagation!
Container-Managed
Variante 1
>Session Bean (Stateful oder Stateless)
>Transaktionsgrenze: Business-Methode (Transaction-Propagation)
>Beeinflussung durch @TransactionAttribute
>Scope:
>Transactional
>Mit Persistence-Context-Propagation
@Stateless public class UserDaoEjb { @PersistenceContext private EntityManager em; …
}
Transaction-Scoped
@Stateless public class UserDaoEjb { @PersistenceContext private EntityManager em; …
}
@Stateless public class UserDaoEjb { @PersistenceContext private EntityManager em; …
public void businessMethod() { // EntityManager available } }
Transaction-Scoped
@Stateless public class UserDaoEjb { @PersistenceContext private EntityManager em; …
}
@Stateless public class UserDaoEjb { @PersistenceContext private EntityManager em; …
public void businessMethod() { // EntityManager available } }
@Stateless public class UserDaoEjb { @PersistenceContext private EntityManager em; … @TransactionAttribute(NEVER) public void businessMethod() { // EntityManager not available } }
Transaction-Scoped
Transaktions-grenze
Vorsicht mit Lazy-Initialization
Java-EE-5 Architektur
Container-Managed
Variante 2
>Stateful Session Bean
>EXTENDED EntityManager
>Transaktionsgrenze
wie bei Variante 1
>Lesen ohne Transaktion
Lazy-Loading möglich
>Scope Session
@Stateful public class UserDaoEjb { @PersistenceContext(type = EXTENDED) private EntityManager em; …
}
@Stateful public class UserDaoEjb { @PersistenceContext(type = EXTENDED) private EntityManager em; …
public void businessMethod() { // EntityManager available } }
@Stateful public class UserDaoEjb { @PersistenceContext(type = EXTENDED) private EntityManager em; … @TransactionAttribute(NEVER) public void businessMethod() { // EntityManager available } }
Session-Scoped
@Stateful public class UserDaoEjb { @PersistenceContext( synchronization = UNSYNCHRONIZED) private EntityManager em; …
}
@Stateful public class UserDaoEjb { @PersistenceContext( synchronization = UNSYNCHRONIZED) private EntityManager em; … public void businessMethod() {
// no flush after invocation } }
@Stateful public class UserDaoEjb { @PersistenceContext( synchronization = UNSYNCHRONIZED) private EntityManager em; … public void businessMethod() { em.joinTransaction(); // flush‘n‘commit after invocation } }
Neu seit JPA 2.1
Unsynchronized
Container-Managed
Zusatz-Feature Java EE 6
>Service-Schicht als EJBs
>DAO-Schicht als CDI Beans
>EJB stellt EntityManager zur Verfügung @Produces
>Injection des EntityManagers in CDI-Beans @Inject
Producer für CDI
@Stateless public class UserServiceEjb { @Produces @PersistenceContext private EntityManager em; … }
public class UserDao { @Inject private EntityManager em; }
Fazit EJB-Integration
>Injection in Session Beans (stateful und stateless)
>Transaktionsgrenze
>Methodenaufruf der Session Bean (inklusive Transaction-Propagation)
>Scopes
>Transaction (alle Session Beans)
>Session (Stateful Session Beans)
public class MyEntityListener { @Inject @Current private User user;
@PrePersist public void setCreator(MyEntity e) { e.setCreator(user); } }
Neu seit JPA 2.1
CDI-Integration
public class MyEntityListener { @Inject @Current private User user;
@PrePersist public void setCreator(MyEntity e) { e.setCreator(user); } }
Achtung:
Verhalten bei Zugriff auf andere Entitäten oder Verändern von Beziehungen ist nicht
standardisiert.
Neu seit JPA 2.1
CDI-Integration
CDI bietet viele Scopes und ausgefeiltes
Lifecycle-Management!
Kann das nicht für den EntityManager genutzt werden?
CDI-Integration außerhalb des Standards
@ApplicationScopedpublic class MyPersistenceUnit { @Produces @RequestScoped public EntityManager createEntityManager( EntityManagerFactory factory) { return factory.createEntityManager(); } …}
Request-Scoped EntityManager
@ApplicationScopedpublic class MyPersistenceUnit { …
public void closeEntityManager( @Disposes EntityManager entityManager) {
entityManager.close(); }}
Lifecycle-Management von CDI
EntityManagerlifecycle
Lazy-Initializationmöglich
Architektur mit CDI
EntityManagerlifecycle
Lazy-Initializationmöglich
Offen:
Transaktionsgrenze
Architektur mit CDI
@Transactional @Interceptorpublic class MyTransactionalInterceptor { @Inject private EntityManager em;
…}
CDI-Interceptor
… @AroundInvoke public Object startTransaction( InvocationContext context) throws … { em.getTransaction().begin(); try { return context.proceed(); } finally { em.getTransaction().commit(); } }
CDI-Interceptor
@ApplicationScopedpublic class MyUserDao { @Inject private EntityManager em;
@Transactional public void persist(User user) { em.persist(user); }}
CDI-Interceptor
@ApplicationScopedpublic class MyPersistenceUnit { @Produces @RequestScoped public EntityManager createEntityManager( EntityManagerFactory factory) { return factory.createEntityManager(); } …}
Weitere Scopes
@ApplicationScopedpublic class MyPersistenceUnit { @Produces @ConversationScoped public EntityManager createEntityManager( EntityManagerFactory factory) { return factory.createEntityManager(); } …}
Conversation-Scope?
@Stateful @ConversationScoped public class UserServiceEjb { @Produces @RequestScoped @PersistenceContext(type = EXTENDED) private EntityManager em; … }
Producer für CDI
@Stateful @ConversationScoped public class UserServiceEjb { @Produces @RequestScoped @PersistenceContext(type = EXTENDED) private EntityManager em; … }
Passivation nur in einigen Containern
Producer für CDI
Asynchronität
By default, session bean invocationsthrough the Remote, Local, and no-
interface views are synchronous.Clients can achieve asynchronous
invocation behavior by invoking session bean methods that
have been designed to support asynchrony.
@Asynchronous
Zitat: EJB Spezifikation, v 3.1
@Asynchronous
EJB 3.1 Asynchronität
>Leichtgewichtig (analog zu Java SE) java.util.concurrent.Future
>Markierung via @Asynchronous>Methodensignatur
>Parameter beliebig
>Rückgabewert Future oder void
@Asynchronous
@Asynchronouspublic void broadcast(List<Email> receipients, String message) { …}
@Asynchronouspublic Future<Result> compute(…) { … return new AsynchResult<Result>(…);}
java.util.concurrent.Future
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;}
@Asynchronous Pro & Contra
>Einfacher Mechanismus >Separate Transaktion >Separater EntityManager
>Potentiell „gefährlich“
>Anzahl gestarteter Threads nicht limitierbar
>Thread Leaks (Threads die nicht enden bis zum
Restart des Containers)
>EntityManager ist Single-Threaded> 7
6
Locking
Locking
Optimistic
>@VersionProgrammatisches Locken
>EntityManager - lock , find , refresh>Query - setLockMode>@NamedQuery - lockMode-Element
Lock-Modes
>READ (deprecated)
>WRITE (deprecated)
>OPTIMISTIC
>OPTIMISTIC_FORCE_INCREMENT
>PESSIMISTIC_READ
>PESSIMISTIC_WRITE
>PESSIMISTIC_FORCE_INCREMENT
>NONE
Caching
Caching
Der First-Level-Cache
>EntityManager-Cache
>Caching von Entitäten
Caching
Der Second-Level-Cache
>EntityManagerFactory-Cache
>Implementierung provider-spezifisch
>Zugriff über Ids
Konfiguration
persistence.xml<persistence ...> <persistence-unit name="Weblog" ...>
<class>...</class>
<shared-cache-mode>ALL</shared-cache-mode>
<!– alternativ --> <properties> <property name="javax.persistence.sharedCache.mode" value="ALL" /> </properties> </persistence-unit></persistence>
SharedCacheMode
>ALL>NONE>ENABLE_SELECTIVE>DISABLE_SELECTIVE>UNSPECIFIED
@Entity @Cachable // = @Cachable(true) public class User { … }
Caching
@Entity @Cachable(false) public class User { … }
Caching
@Entity // Keine Cache-Annotation public class User { … }
Caching
Caching
EntityManagerFactory.getCache()Abfragen und Löschen des Cache-Inhalts
Beeinflussen des Cache-Verhaltens bei einzelnen Operationen:
>javax.persistence.cache.retrieveMode>javax.persistence.cache.storeMode
Caching
>CacheRetrieveMode
>USE (default)
>BYPASS>CacheStoreMode
>USE (default)
>BYPASS>REFRESH
JPA vs. NoSQL
JPA vs. NoSQL
JPA
>Halten der Daten im Heap
>Verwaltung von Objekten mit Klassenstruktur (Java-Objekte)
NoSQL
>Verwalten großer Datenmengen
>Verwalten von unstrukturierten Objekten (Dokumente, JSON-Objekte)
JPA und NoSQL
JPA und NoSQL
>Abfragen beider Datastores
>Relational für strukturierte Daten
>NoSQL für unstrukturierte Daten
>Zusammenführen beider Ergebnisse im Speicher
>Übertragen der NoSQL-Daten in Map
Vielen Dank für Ihre Zeit.
Kontakt:
open knowledge GmbHBismarckstr. 1326122 Oldenburg
@ArneLimburg @_openknowledge
Questions & Answers