270 likes | 381 Views
Architecture. Persistente Domänenmodelle mit JPA 2.0 und Bean Validation. Java EE 6 Integration. JSF 2.0 und Bean Validation. < h:inputText id =" name " value ="#{employeeBean.employee.name}"> < f:validateBean for =" name "/> </ h:inputText > < h:message for =" name " />.
E N D
Architecture Persistente Domänenmodelle mit JPA 2.0 und Bean Validation
JSF 2.0 und Bean Validation <h:inputTextid="name" value="#{employeeBean.employee.name}"> <f:validateBeanfor="name"/> </h:inputText> <h:messagefor="name" />
JSF und NULL Werte • Leere Input Felder werden von JSF als «» (leerer String) behandelt • Dies ist evtl. ein Problem für die BeanValidation • @NotNullConstraint 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»!
EJB 3.x DependencyInjection • @PersistenceUnit • Liefert eine Referenz zur EntityMangerFactory • @PersistenceContext • Liefert eine Referenz zum EntityManger @PersistenceUnit private EntityManagerFactoryemf; @PersistenceContext private EntityManagerem;
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
Stateless Session Beans @Stateless publicclassEmployeeService { @PersistenceContext(type=PersistenceContextType.TRANSACTION) private EntityManagerem; public Employee findById(Integer id) { Employee e = em.find(Employee.class, id); return e; } }
Stateful Session Beans @Stateful publicclassEmployeeService { @PersistenceContext(type=PersistenceContextType.EXTENDED) private EntityManagerem; public Employee findById(Integer id) { Employee e = em.find(Employee.class, id); return e; } }
Persistence Unit <persistence> <persistence-unitname="jpa.sbb" transaction-type="JTA"> <jta-data-source>jdbc/emp</jta-data-source> </persistence-unit> </persistence>
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
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 und JPA • Häufig überflüssig dank EntityManager • Wenn schon DAO dann generisch publicinterfaceGenericDAO { <T extendsBaseEntity> T create(T t); <T extendsBaseEntity> T find(Class<T> type, Serializableid); <T extendsBaseEntity> T update(T t);voiddelete(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 ConstructorExpressions existiert eine einfache Möglichkeit direkt aus den Resultaten Transfer Objects zu erzeugen. publicclassEmpMenu {publicEmpMenu(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 (EmpMenumenu : result) { log.info(menu.employeeName + "," + menu.deptName);}
Schichtung und Verteilung 3rd Party Applikation XY Ausschliesslich TOs Client oder Webserver Applikationsserver DB Server Service Fassade Präsentations-schicht Business Datenhaltung Datenzugriff TOs und Entities
Client/Server Entities Ausschliesslich TOs
Java Persistence API 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.
Java Persistence API Open Session in View Lazy O1 O1 HTTP Session Transaktion 1 Transaktion 2
Java Persistence API 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.
Java Persistence API Preload Pattern nach Jürgen Kohl (2) publicclassPreload { private Class modelClass; private String property; publicPreload(Class modelClass, String property) {this.modelClass = modelClass; this.property = property; }}
Java Persistence API Preload Pattern nach Jürgen Kohl (3) publicabstractclassGenericDAO<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 ObjectinvokeGetter(Objectentity, Preloadpreload) { String getterName = getPropertyGetterName(preload.getProperty()); try { Methodmethod = preload.getModelClass().getMethod(getterName, (Class[]) null); returnmethod.invoke(entity, (Object[]) null); } catch (Exception ex) { thrownewRuntimeException("Can'tinvokegetterforproperty: " + preload.getProperty(), ex); } }
Java Persistence API Preload Pattern nach Jürgen Kohl (4) // Fortsetzung GenericDAO protectedvoidpreload(Objectentity, Preload[] preloads) { if (entityinstanceofCollection) { for (ObjectresultEntity : (Collection) entity) { preload(resultEntity, preloads); } } else { for (Preloadpreload : preloads) { if (preload.getModelClass().isInstance(entity)) { ObjectgetterResult = invokeGetter(entity, preload); preload( getterResult, preloads); } } }
Java Persistence API Preload Pattern nach Adam Bien • Idee: VerwendungJXPath für die Initiatlisierung von LazyLoading Beziehungen • Beispielklasse mit LazyOneToOne Beziehung: • Aufruf der Getter-Methode mit JXPath: @Entitypublicclass 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