Architecture
Persistente Domänenmodelle mit JPA 2.0 und Bean Validation
2
Java EE 6 Integration
3
JSF 2.0 und Bean Validation
<h:inputText id="name" value="#{employeeBean.employee.name}"> <f:validateBean for="name"/></h:inputText>
<h:message for="name" />
4
JSF und NULL Werte
Leere Input Felder werden von JSF als «» (leerer String) behandelt
Dies ist evtl. ein Problem für die BeanValidation @NotNull Constraint greift nicht
Lösung:<context-param> <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name> <param-value>true</param-value> </context-param>
Achtung vor allfälligen «Nebenwirkungen»!
5
EJB 3.x Dependency Injection
@PersistenceUnit Liefert eine Referenz zur EntityMangerFactory
@PersistenceContext Liefert eine Referenz zum EntityManger
@PersistenceContextprivate EntityManager em;
@PersistenceUnitprivate EntityManagerFactory emf;
6
JPA 2.0 und JSR 303
@NotNull statt @Column(nullable=false) @Size.max statt @Column.length @Digits statt @Column.precision/.scale @Min / @Max bei numerischen Columns @Future / @Past bei Datumstypen @Size für Collections und Arrays
7
Stateless Session Beans
@Statelesspublic class EmployeeService {
@PersistenceContext( type=PersistenceContextType.TRANSACTION) private EntityManager em;
public Employee findById(Integer id) { Employee e = em.find(Employee.class, id);
return e; }}
8
Stateful Session Beans
@Statefulpublic class EmployeeService {
@PersistenceContext( type=PersistenceContextType.EXTENDED) private EntityManager em;
public Employee findById(Integer id) { Employee e = em.find(Employee.class, id);
return e; }}
9
Persistence Unit
<persistence> <persistence-unit name="jpa.sbb" transaction-type="JTA">
<jta-data-source>jdbc/emp</jta-data-source>
</persistence-unit></persistence>
10
JPA 2.0 und JSR 303
@NotNull statt @Column(nullable=false) @Size.max statt @Column.length @Digits statt @Column.precision/.scale @Min / @Max bei numerischen Columns @Future / @Past bei Datumstypen @Size für Collections und Arrays
11
Patterns
Data Access Object (DAO)
ProblemYou want to encapsulate data access and manipulation in a separate layer
SolutionUse a Data Access Object to encapsulate all access to the persistent store. The Data Access Object manages the connection with the data source to obtain and store data
DAOs werden als einfache, zustandslose Klassen realisiert, die den Zugriff auf eine Datenquelle kapseln
Ziele: Trennung von Business Logik und technischer Zugriffslogik Kapselung von Zugriff auf Datenbank oder Fremdsystem
DAO Klassendiagramm
Client Data Access Object Datenquelle
ResultSetTransfer Object
uses
uses createscreates
accesses
creates /uses
DAO und JPA
Häufig überflüssig dank EntityManager Wenn schon DAO dann generisch
public interface GenericDAO { <T extends BaseEntity> T create(T t); <T extends BaseEntity> T find(Class<T> type, Serializable id); <T extends BaseEntity> T update(T t); void delete(Object t); List findByNamedQuery(String queryName); List findByNamedQuery(String queryName, Map<String, Object> parameters);}
Transfer Object (TO)
ProblemYou want to transfer multiple data elements over a tier
SolutionUse a Transfer Object to carry multiple data across a tier
Daten werden in Datencontainern zusammengefasst und über Tier-Grenzen transportiert
Das Konzept sollte generell zwischen den Schichten eingesetzt werden
Ziel: Verringerung der übertragenen Datenmenge
TO und JPA
Mit Constructor Expressions existiert eine einfache Möglichkeit direkt aus den Resultaten Transfer Objects zu erzeugen. public class EmpMenu { public EmpMenu(String employeeName, String deptName){...}}
List result = em.createQuery( "SELECT NEW jpa.util.EmpMenu(e.name, e.department.name) " + "FROM Project p JOIN p.employees e " + "where p.name = "ZLD").getResultList();
for (EmpMenu menu : result) { log.info(menu.employeeName + "," + menu.deptName);}
Schichtung und Verteilung
Applikationsserver DB Server
Datenzugriff
Business
Client oder Webserver
Präsentations-schicht
Datenhaltung
Service Fassade
3rd Party
Applikation XY
TOs und Entities
Ausschliesslich TOs
Client/Server
Ausschliesslich TOs
Entities
Lazy Loading und Verteilung
Lazy Loading deaktivieren Vorteil: einfach, für wenig Daten nutzbar Nachteil: bei vielen Daten aus Performance Gründen nicht nutzbar
Open Session in View Pattern geht davon aus, dass in einer Webappliaktion die Session erst geschlossen wird, wenn der Request beendet ist. Vorteil: Die Daten werden bei Bedarf nachgeladen Nachteil: Funktioniert nur bei einer Webapplikation, Änderungen der Daten
auf dem GUI-Level unter Umgehung der Businesslogik, saubere Trennung der Layer nicht möglich.
Preload Pattern: Der Aufrufer einer DAO-Methode übergibt Informationen, welche Daten vorgeladen werden sollen. Vorteil: Abhängig von der Situation kann Lazy Loading eingesetzt oder
umgangen werden Nachteil: Kann zu einer Schwemme von DAO-Methoden führen um die
möglichen Kombinationen anzubieten.
19Java Persistence API
Open Session in View
20Java Persistence API
HTTP Session
Transaktion 1 Transaktion 2
O1 O1
Lazy
Preload Pattern nach Jürgen Kohl (1)
Jürgen Kohl beschreibt in der Ausgabe 4/2008 des Java Magazins sein Preload Pattern, dass in einigen Praxisprojekten erfolgreich eingesetzt wurde. Hierbei liegt der Fokus darauf die Methodenschwemme zu vermeiden und weiterhin die Nutzung eines GenericDAO zu erlauben.
Seine Anforderungen sind: generischer Mechanismus keine Verkomplizierung der Backend-Methoden bei
steigender Komplexität des Domain Models Anwender der Backend-Methoden können denkbare
Preload-kombinationen selbst definieren.
21Java Persistence API
Preload Pattern nach Jürgen Kohl (2)
22Java Persistence API
public class Preload { private Class modelClass; private String property;
public Preload(Class modelClass, String property) { this.modelClass = modelClass; this.property = property; }}
Preload Pattern nach Jürgen Kohl (3)
23Java Persistence API
public abstract class GenericDAO<T, ID> {
private Class<T> persistentClass;
private String getPropertyGetterName(String property ) { String propertyUpper = property.toUpperCase().substring(0, 1); return "get" + propertyUpper + property.substring(1); }
private Object invokeGetter(Object entity, Preload preload) { String getterName = getPropertyGetterName(preload.getProperty()); try { Method method = preload.getModelClass().getMethod( getterName, (Class[]) null); return method.invoke(entity, (Object[]) null); } catch (Exception ex) { throw new RuntimeException("Can't invoke getter for property: " + preload.getProperty(), ex); } }
Preload Pattern nach Jürgen Kohl (4)
24Java Persistence API
// Fortsetzung GenericDAO
protected void preload(Object entity, Preload[] preloads) { if (entity instanceof Collection) { for (Object resultEntity : (Collection) entity) { preload(resultEntity, preloads); } } else { for (Preload preload : preloads) { if (preload.getModelClass().isInstance(entity)) { Object getterResult = invokeGetter(entity, preload); preload( getterResult, preloads); } }}
Preload Pattern nach Adam Bien
Idee: Verwendung JXPath für die Initiatlisierung von Lazy Loading Beziehungen
Beispielklasse mit Lazy OneToOne Beziehung:
Aufruf der Getter-Methode mit JXPath:
25Java Persistence API
@Entitypublic class Master { ... @OneToOne(fetch=FetchType.LAZY) private Detail detail; ...}
JXPathContext.newContext(master).getValue("/detail");
Batch
Problematik Sequenzielle Verarbeitung von grossen
Datenmengen Verwendung von Cursors mit JPA nicht möglich
Lösung Pagination
Vorsicht Persistence Context kann gross werden und
sollte regelmässig aufgeräumt werden (EntitManager.clear())
Clustering
Zur Erhöhung der Verfügbarkeit und der Verbesserung der Performance werden Application Server häufig geclustert
Problem Persistence Context auf mehreren Knoten
vorhanden Synchronität mit Datenbank nicht gegeben
Lösung Synchronisation (Providerabhängig) Referesh der Entitäten bei Bedarf