Folien zum Vortrag JSF und JPA effizient kombinieren von Michael Kurz auf der W-JAX 2011 in München.
Die dazugehörenden Beispiele sind unter https://github.com/jsflive/mymail-owb zu finden.
2. Agenda
• Motivation
• Architektur mit/ohne EE 6-Container
• Integration von JPA und JSF
• Demonstration anhand eines Beispiels:
– MyMail 01: Architektur, JPA, JSF
– MyMail 02: Paging
– MyMail 03: Selektion
3. Motivation
• Große Datenmengen effizient darstellen
• Übersichtlichkeit durch Paging
• Performance durch gezieltes Laden aus DB
• Selektion einzelner Zeilen
• JPA und JSF ermöglichen mit Bordmitteln
eine einfache und performante Lösung
• Leichtgewichtige Java EE 6-Architektur
4. Architektur und Technologie-Stack
Architektur Mit EE 6-Container Ohne EE 6-Container
Präsentation JSF, CDI, CODI JSF, CDI, CODI
Service EJB, CDI CDI, CODI
Datenzugriff EJB, JPA CDI, CODI, JPA
5. MyFaces Extensions CDI
• Portable Erweiterung für CDI
• Vereinfacht Arbeit mit CDI und JSF
• Mehrere Module:
– JSF 1.2 und 2.0
– JPA, Bean-Validation, Message, Scripting...
• Funktioniert mit:
– CDI: OpenWebBeans, Weld
– JSF: MyFaces, Mojarra (1.2 und 2.0)
• Stabile Version 1.0.1 verfügbar
6. Verwaltung des Persistenzkontexts
• Mit Java EE-Container
– EntityManager vom Container verwaltet
@javax.ejb.Stateless
public class CustomerRepository {
@PersistenceContext private EntityManager em;
}
• Ohne Java EE-Container (mit CDI)
– EntityManager von MyFaces CODI verwaltet
@ApplicationScoped
public class CustomerRepository {
@Inject private EntityManager em;
}
7. Verwaltung von Transaktionen
• Mit Java EE-Container
– Transaktionen vom Container verwaltet
– Steuerung über @TransactionAttribute
• Ohne Java EE-Container (mit CDI)
– Transaktionen von MyFaces CODI verwaltet
@ApplicationScoped
public class CustomerService {
@Inject
private CustomerRepository customerRepository;
@Transactional
public void saveUser(User user) {...}
}
9. Abfragen mit JPQL
• JPA 1: Stringbasierte Abfragen mit JPQL
String queryStr = "select m from Mail m where
m.subject like :subj order by m.date desc";
• Ausführen der Abfrage mit EntityManager
Query query = em.createQuery(queryStr);
query.setParameter("subj", "%108_");
return query.getResultList();
• Nachteile:
– Überprüfung erst zur Laufzeit
– Dynamische Abfragen komplex
10. Abfragen mit Criteria API 1/2
• JPA 2: Abfragen mit dem Criteria API
• CriteriaBuilder ist Einstiegspunkt
CriteriaBuilder b = em.getCriteriaBuilder();
• CriteriaQuery ist Container für Abfrage
CriteriaQuery<Mail> c = b.createQuery(Mail.class);
• CriteriaQuery bietet Methoden für Klauseln
Root<Mail> root = c.from(Mail.class);
c.select(root);
c.where(b.like(root.<String>get("subject"),
b.parameter(String.class, "subj")));
c.orderBy(b.desc(root.get("date")));
11. Abfragen mit Criteria API 2/2
• Ausführen der Abfrage mit EntityManager
TypedQuery<Mail> query = em.createQuery(crit);
query.setParameter("subj", "%108_");
return query.getResultList();
• Vorteile:
– Typsicher
– Statisch überprüfbar zur Kompilierzeit
– Abfragen mit Java-Sprachmittel bauen
– Flexibler für dynamische Abfragen
– Mehr Funktionalität im Vergleich zu JPQL
12. Typsicherheit mit dem JPA-Metamodell
• JPA 2: Statisches, kanonisches Metamodell
– Eine Metamodell-Klasse pro verwalteter Klasse
– Wird im Normallfall generiert
@StaticMetamodel(Mail.class)
public class Mail_ {
public static volatile
SingularAttribute<Mail, String> subject;
public static volatile
ListAttribute<Mail, Attachment> attachments;
}
c.where(b.like(root.get(Mail_.subject),
b.parameter(String.class, "subj")));
c.orderBy(b.desc(root.get(Mail_.date)));
14. Wie funktioniert h:dataTable?
• Anzuzeigende Daten im Attribut value
– Arrays, Listen, ResultSet, DataModel
• Zeilenweise Abarbeitung der Daten
• Ein Datenelement wird zu einer Zeile
• Variablenname aktueller Zeile in Attribut var
• h:column für jede Zeile ausgeführt
<h:dataTable var="mail" value="#{mailBean.mails}">
<h:column>
<h:outputText value="#{mail.subject}"/>
</h:column>
</h:dataTable>
15. Paging mit JSF
• h:dataTable unterstützt bereits Paging
– Attribut first: erste Zeile der Page
– Attribut rows: Größe der Page
item00
item01 Property 1 Property 2
item02
first="2"
item03
item02.prop1 item02.prop2
rows="2"
item04 item03.prop1 item03.prop2
item05
item06
• Paginator-Komponente um first zu setzen
– Keine Komponente im Standard
• Problem: Nur Daten für Page aus DB laden
16. Klasse DataModel in JSF
• javax.faces.model.DataModel<E>
– public void setRowIndex(int rowIndex)
– public E getRowData()
– public int getRowCount()
– public int getRowIndex()
– public boolean isRowAvailable()
– public Object getWrappedData()
– public void setWrappedData(Object data)
• Eigene Variante PageableDataModel<E>
– Lädt und cacht Daten für aktuelle Page
18. DataModel mit Selektion
• SelectableDataModel mit Set für Selektion
public class SelectableDataModel<T>
extends PageableDataModel<T> {
private Set<T> selectedObjects;
public Set<T> getSelectedObjects() {...}
public Map<T, Boolean> getRowSelected() {...}
}
• Selektion in Seite via Map
<h:dataTable var="mail" value="#{mailBean.mails}">
<h:column>
<h:selectBooleanCheckbox
value="#{mailBean.mails.rowSelected[mail]}"/>
</h:column>
</h:dataTable>
20. Fazit
• Leichtgewichtige Entwicklung mit Java EE 6
• Mit/Ohne EE 6-Container möglich
• Kombination von JPA und JSF bereits
mit Bordmitteln sehr mächtig
• Standardisierte Lösung
• JPA 2 Criteria API flexibel, aber komplex