java persistence in action – einsatz von jpa 2.x in java-ee-anwendungen
DESCRIPTION
Präsentation zum Vortrag von Dirk Weil (GEDOPLAN, http://www.gedoplan.de) auf der W-JAX 2012: Der überwiegende Teil von Unternehmensanwendungen nutzt O/R-Mapper zum Zugriff auf Datenbanken. Reicht dafür der Standard JPA 2.0 oder braucht man zwingend spezielle Eigenschaften von Hibernate/EclipseLink/OpenJPA? Soll man transaktionsgebunden oder konversationsorientiert arbeiten? Welche Neuerungen sind für JPA 2.1 geplant? Dieser Talk beantwortet die Fragen anhand diverser Beispiele aus der Praxis.TRANSCRIPT
Java Persistence in ActionEinsatz von JPA 2.x in Java-EE-Anwendungen
Dirk Weil | GEDOPLAN
Dirk Weil• GEDOPLAN GmbH, Bielefeld• Java EE seit 1998• Konzeption und
Realisierung• Vorträge• Seminare• Veröffentlichungen
Java Persistence in Action2
Java Persistence in EE-Anwendungen• Basics:
– META-INF/persistence.xml referenziert Datasource
– EntityManager-Bereitstellung per Injektion
<persistence …>
<persistence-unit name="beantrial">
<jta-data-source>jdbc/beantrial</jta-data-source>
…
@PersistenceContext(unitName = "beantrial")
EntityManager entityManager;
Java Persistence in Action4
Java Persistence in EE-Anwendungen• The CDI way: public class EntityManagerProducer {
@PersistenceContext(unitName = "beantrial")
@Produces
EntityManager entityManager;
public class PersonRepository {
@Inject
EntityManager entityManager;
public class FirmaRepository {
@Inject
EntityManager entityManager;
public class ProjektRepository {
@Inject
EntityManager entityManager;
Java Persistence in Action5
Entity Manager Lifecycle• EM-Lebensdauer � Entity-Management-Zeit
– Entities werden detached– Nachladen von Lazy-Werten etc. nicht mehr möglich
alle evtl. benötigten Werte müssen geladen sein
• Remote-Anwendung (Swing, Java FX, …)– Entities im Client sind stets detached
• Web-Anwendung (JSF, …)– Render-Phase, Folgerequests?
Java Persistence in Action6
Entity Manager Lifecycle
@Model
public class PersonModel {
public List<Person> getPersonen() {
… personRepository.findAll() …
<h:dataTable value="#{personModel.personen}" var="person">
…
<h:… value="#{person.hobbies}" />
…
@Entity
public class Person {
@ElementCollection(fetch = FetchType.LAZY)
private List<String> hobbies;
LazyLoadException
?
Java Persistence in Action7
Entity Manager Lifecycle• EM-Optionen:
– Transaction-scoped– (Extended)*– Application-managed
• Patterns– Eager Loading– Open Entity Manager in View– Conversational Entity Manager
*nur Stateful EJBs – nicht weiter betrachtet
Java Persistence in Action8
verschiedene Kombinationen möglich
Transaction-scoped Entity Manager• Standard für container-managed Entity Manager
@Transactional
public class PersonRepository {
@Inject
EntityManager entityManager;
public void persist(Person entity) { … }
public List<Person> findAll() { … }
public class EntityManagerProducer {
@PersistenceContext(unitName = "beantrial")
@Produces
EntityManager entityManager;
Java Persistence in Action9
Transaction-scoped Entity Manager• Start: Erste Nutzung des EM in einer TX• Ende: TX-Ende � alle Entities werden detached
• TX-Steuerung– per CDI Interceptor @Transactional
– per EJB– mittels UserTransaction
– häufig für (Teil-)Geschäftsprozesse(nur für CRUD wäre ein Antipattern)
Java Persistence in Action10
Transaction-scoped Entity Manager
@Model
public class PersonModel {
public List<Person> getPersonen() {
… personRepository.findAll() …
<h:dataTable value="#{personModel.personen}" …>
…
<h:… value="#{person.hobbies}" />
@Transactional
public class PersonRepository {
public List<Person> findAll() { … }
DB
View
Model
ServiceTX
deta
ched
man
aged
Java Persistence in Action11
Transaction-scoped Entity Manager• Eager Loading nötig
– deklarativ
– oder durch explizitenZugriff
@ElementCollection(fetch = FetchType.EAGER)
private List<String> hobbies;
public void loadLazyAttributes() {
this.hobbies.size();
Java Persistence in Action12
Transaction-scoped Entity Manager• Code Browsing:
Personenverwaltung mit detached Entities
Java Persistence in Action13
(Download: www.gedoplan.de� Blog
Transaction-scoped Entity Manager• Alternative: Transaction per Request
@Model
public class PersonModel {
public List<Person> getPersonen() {
… personRepository.findAll() …
<h:dataTable value="#{personModel.personen}" …>
…
<h:… value="#{person.hobbies}" />
View
Model
TX
…
Java Persistence in Action14
Transaction-scoped Entity Manager• Transaction per Request
– mittels Servlet Filter– mittels JSF Phase Listener
– Seam Transaction / Seam FacesAchtung: Per Default aktiv !(Abschalten: Siehe www.gedoplan.de� Blog)
Java Persistence in Action15
Transaction-scoped Entity Manager• Einfaches Verfahren
• Eager Loading– ist aufwändig– hebelt Lazy-Optimierungen aus
• TX per Request– durchbricht 'normale' Architektur– nicht für Multi-Request-Fälle geeignet
Java Persistence in Action16
Application-managed Entity Manager• Nutzung von EntityManagerFactory
• Lifecycle wird von Anwendung bestimmtpublic class EntityManagerProducer {
@PersistenceUnit(unitName = "beantrial")
EntityManagerFactory entityManagerFactory;
@Produces
public EntityManager createEntityManager() {
return this.entityManagerFactory.createEntityManager();
}
public void closeEntityManager(@Disposes @Any EntityManager em) {
if (em.isOpen())
em.close();
}
Java Persistence in Action17
Application-managed Entity Manager• persist, merge, remove auch ohne TX erlaubt
• Änderungen werden erst durch Commit abgelegt• Entity Manager nicht automatisch TX-gebunden
@Transactional
public void saveAll() {
this.entityManager.joinTransaction();
}
Java Persistence in Action18
Request-scoped Entity Manager• Request
Scope= Open Entity Manager in View
@Produces
@RequestScoped
public EntityManager createEntityManager() {
Ent
ityM
anag
er
@Model
public class PersonModel {
public List<Person> getPersonen() {
… personRepository.findAll() …
<h:dataTable value="#{personModel.personen}" …>
…
<h:… value="#{person.hobbies}" />
@Transactional
public class PersonRepository {
public void saveAll() { … }
DB
View
Model
Service
TX
Java Persistence in Action19
EM
EM
Conversation-scoped Entity Manager• Conversational
Entity Manager
@Produces
@ConversationScoped
public EntityManager createEntityManager() {
Request 2Conversation.begin()
Request 1
Request 3
Request 4Conversation.end()
Java Persistence in Action20
Conversation-scoped Entity Manager• ABER:
– Conversation Scope ist passivating– Objekte in ihm müssen serialisierbar sein– EntityManager nicht notwendigerweiseSerializable
• Abhilfe: Serializable-Wrapper
Java Persistence in Action21
Conversation-scoped Entity Manager• Serializable-Wrapper:
@Produces
@ConversationScoped
public EntityManager createEntityManager() {
EntityManager em = entityManagerFactory.createEntityManager();
if (!(em instanceof Serializable)) {
ClassLoader loader = em.getClass().getClassLoader();
Class<?>[] ifs = { EntityManager.class, Serializable.class };
EMInvocationHandler handler = new EMInvocationHandler(em);
em = (EntityManager) Proxy.newProxyInstance(loader, if, handler);
}
return em;
}
Java Persistence in Action22
Conversation-scoped Entity Manager• Serializable-Wrapper:
class EMInvocationHandler implements InvocationHandler, Serializable {
private transient EntityManager em;
public EntityManagerInvocationHandler(EntityManager em) {
this.em = em;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (this.entityManager == null)
throw new IllegalStateException("EntityManager is null");
return method.invoke(this.em, args);
}
}
Java Persistence in Action23
Conversation-scoped Entity Manager• Serializable-Wrapper:
– Prinzip Hoffnung:Wahrscheinlich keine Passivierung;wenn doch, ordentliche Fehlermeldung.
– Ungeeignet für Replikation
Java Persistence in Action24
Conversation-scoped Entity Manager• Code Browsing:
Personenverwaltung mit attached Entities
Java Persistence in Action25
(Download: www.gedoplan.de� Blog
SQL-Zugriffe im Beispiel
Aktion TX-scoped EM Conv-scoped EM
Anzeige aller PersonenSELECT ID, ... FROM PERSON dito
Auswahl einer Person zum EditierenSELECT ID, ... FROM PERSON WHERE (ID = ?)
SELECT TEL, ... FROM PERSON_TELWHERE (PERSON_TEL.Person_ID = ?)
dito
SELECT HOBBY, ... FROM PERSON_HOBBYWHERE (PERSON_HOBBY.Person_ID = ?)
Java Persistence in Action26
SQL-Zugriffe im Beispiel
Aktion TX-scoped EM Conv-scoped EM
Ändern des Vornamens und Speichern der PersonSELECT ID, ... FROM PERSONWHERE (ID = ?)
SELECT TEL, ... FROM PERSON_TELWHERE (PERSON_TEL.Person_ID = ?)
SELECT HOBBY, ... FROM PERSON_HOBBYWHERE (PERSON_HOBBY.Person_ID = ?)
UPDATE PERSON SET VORNAME = ?WHERE (ID = ?)
dito
Java Persistence in Action27
TX- oder Conversation-scoped?• Transaction-scoped EM
� einfaches Vorgehen� speichert nur das, was explizit übergeben wird
• Conversation-scoped EM� vermeidet Lazy-Load-Effekte im Web-Anwendungen� erzeugt weniger DB-Zugriffe
� speichert immer alle Änderungen
� benötigt mehr Speicher� funktioniert nicht im Cluster oder für Remote-Clients
Java Persistence in Action28
JPA 2.1 – What's coming?• Query-Erweiterungen
– ON: Join-Filter
– TREAT: Downcast (inkl. Filterung)
– FUNCTION: Aufruf von DB-Funktionen
select p.name, count(b)
from Publisher p
left join p.books b
on b.bookType = BookType.PAPERBACK
group by p.name
select s from StorageLocation s
where treat(s.product as Book).bookType = BookType.HARDCOVER
select c from Customer c
where function('hasGoodCredit', c.balance, c.creditLimit)
Java Persistence in Action29
JPA 2.1 – What's coming?• Bulk Update/Delete für Criteria Query
• Stored Procedure Queries
CriteriaUpdate<Product> criteriaUpdate
= criteriaBuilder.createCriteriaUpdate(Product.class);
Root<Product> p = criteriaUpdate.from(Product.class);
Path<Number> price = p.get(Product_.price);
criteriaUpdate.set(price, criteriaBuilder.prod(price, 1.03));
entityManager.createQuery(criteriaUpdate).executeUpdate();
StoredProcedureQuery query
= entityManager.createStoredProcedureQuery("findMissingProducts");
Java Persistence in Action30
JPA 2.1 – What's coming?• ConstructorResult für native Queries
Query query = entityManager.createNativeQuery(
"select name, price from product", "NameAndPriceResult");
List<NameAndPrice> result = query.getResultList();
@Entity
@SqlResultSetMapping(name = "NameAndPriceResult",
classes = { @ConstructorResult(
targetClass = NameAndPrice.class,
columns = { @ColumnResult(name = "name"),
@ColumnResult(name = "price")
})
})
public abstract class Product {
public class NameAndPrice {
public NameAndPrice(String name, double price)
Java Persistence in Action31
JPA 2.1 – What's coming?• Konverter
@Converter
public class YesNoConverter implements AttributeConverter<Boolean, String> {
public String convertToDatabaseColumn(Boolean fieldValue) {
if (fieldValue == null) return null;
return fieldValue ? "Y" : "N";
}
public Boolean convertToEntityAttribute(String columnValue) {
if (columnValue == null) return null;
return columnValue.equals("Y");
@Entity
public abstract class Product
{
@Convert(converter = YesNoConverter.class)
private boolean active;
Java Persistence in Action32
JPA 2.1 – What's coming?• CDI Injection in Entity Listener
public class CountryListener {
@Inject
private AuditService auditService;
@PreUpdate
public void preUpdate(Object entity) {
this.auditService.logUpdate(entity);
}
@Entity
@EntityListeners(CountryListener.class)
public class Country {
Java Persistence in Action33
JPA 2.1 – What's coming?• Unsynchronisierter Persistence Context
– keine automatische TX-Bindung– API zum Binden an TX:EntityManager.joinTransaction()EntityManager.isJoinedWithTransaction()
@PersistenceContext(
name = "default",
synchronization = SynchronizationType.UNSYNCHRONIZED)
private EntityManager entityManager;
Java Persistence in Action34
JPA 2.1 – What's coming?• Dynamische Erzeugung von Named Queries• DDL Handling
Java Persistence in Action35
JPA 2.1 – (bisher nur) vorgeschlagen• Fetch groups• Immutable attributes, readonly entities• UUID generator• Multitenancy• Dirty detection• Mapping between JPQL and criteria queries.
Java Persistence in Action36
JPA 2.1 – Reicht das?• Ja
– für neue Anwendungen– in 90%+ der Fälle
• Aber:– Legacy Databases– Performance
Java Persistence in Action37
JPA 2.1 – Reicht das?• Legacy Databases
EclipseLink Hibernate OpenJPA
Optimistic Locking ohne Version � �
weitere Generatoren � � �
Inheritance ohne DiskriminatorColumn
� � �
Polymorphe Assoziationen � �
Custom CRUD SQL � �
…
Java Persistence in Action38
JPA 2.1 – Reicht das?• Performance
EclipseLink Hibernate OpenJPA
R/O Entities � �
Index � � �
Eager Load Tuning � � �
Batch Fetching � �
Partitioning � �
Cascading Delete in DB � �
…
Java Persistence in Action39
JPA 2.1 – Reicht das?• Wunschliste ist nicht leer
– Vieles bereits proprietär vorhanden
• Portabilität ist hohes Gut� Standard weiter entwickeln!
Java Persistence in Action40
Schön, dass Sie da waren!
Di, 20:30: Java EE Day Panel
Mi, 16:30: Gute Zeilen, schlechte ZeilenMi, 19:45: Java on Tracks