Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Objektrelationales Mapping mit JPA 2.0

Ähnliche Präsentationen


Präsentation zum Thema: "Objektrelationales Mapping mit JPA 2.0"—  Präsentation transkript:

1 Objektrelationales Mapping mit JPA 2.0
Jonas Bandi Simon Martinelli 1

2 Simon Martinelli blog: Geschäftsführer der simas GmbH Nebenamtdozent an der Berner Fachhochschule (SWS und Medical Technology Center) Software Architekt, Entwickler, Berater und Trainer im Java EE Umfeld mit mehr als 15 Jahren Erfahrung in der Software Entwicklung

3 Jonas Bandi email: jonas.bandi@gmail.com
blog: Studium der Elektrotechnik und Informationstechnologie an der ETH Zürich Angestellt bei TechTalk Software AG in Zürich Seit 10 Jahren in verschiedenen Bereichen der Softwareentwicklung tätig Mein besonderes Interesse gilt dem Einsatz von modernen Technologien und Methodiken in der Entwicklung von Enterprise Applikationen.

4 Kursziele Einführung in Objekt-Relationales Mapping Einführung in JPA
Fortgeschrittene Themen beim Einsatz von JPA Unterschiede JPA / JPA2 Wir versuchen möglichst neutral bezüglich des verwendeten JPA Providers zu sein Übungen: EclipseLink, Maven Kursunterlagen:

5 Über euch Wer setzt Objekt-Relationales Mapping ein?
Wer arbeitet mit JPA? Wer setzt Hibernate ein? Was setzten die andern ein? Ist JPA 2 ein Thema? Erwartungen an diesen Workshop? Wer hat die Übungen angeschaut?

6 Objektrelationales Mapping mit JPA 2.0
Einführung ORM & JPA Objektrelationales Mapping mit JPA 2.0 6

7 Silver Bullet oder unnötiger Overhead?
Objektrelationales Mapping ist ein umstrittenes Thema, das immer wieder Anlass für hitzige Diskussionen bietet. Hier zwei Beispiele... 7

8 Flame Wars “The Best ORM is No ORM” “The database is an object.”
“ORM is, for the most part, solving the wrong problem. In fact the problem does not really exist.” 8

9 The Vietnam of Computer Science
Ted Neward: “Object/Relational Mapping is the Vietnam of Computer Science” (Juni 2006). Original Blog Post: PDF: “Law of Diminishing Returns” Schnelle anfängliche Erfolge führen zu grossen Erwartungen Mit dem Fortschreiten des Unterfangens werden die Erfolge aber immer spärlicher und die dafür notwendigen Investitionen immer grösser Schlussendlich werden die Investitionen nicht mehr durch den Gewinn gerechtfertigt. Aber es gibt keinen Weg zurück... 9

10 Ausgangslage Domain Model
Anwendung von bewährten OO-Techniken für die Implementation der Geschäftslogik: Kapselung, Vererbung, Polymorphie, Design Patterns ... OO verspricht: bessere Skalierung bei zunehmender Komplexität bessere Erweiterbarkeit bessere Wartbarkeit weniger Redundanz 10

11 Ausgangslage: Relationale Datenbanken
Vorherrschende Technologie zum Speichern von Daten im Enterprise-Umfeld. Gründe: Grosse Investitionen Bewährte, ausgereifte Technologie Flexibilität, Applikationsunabhängigkeit Daten leben länger als Applikationen Optimierte Konzepte zum Speichern von Daten 11

12 Ausgangslage: Der Konflikt
De facto Standard-Konstellation für Enterprise-Applikationen: OO-Technologie für die Applikations- entwicklung Relationale Datenbanken für die Persistenz der Daten An dieser Ausgangslage wird sich mittelfristig kaum etwas ändern. Der OO-Ansatz und der relationale Ansatz weisen grundsätzliche Unterschiede auf Aus diesen Unterschieden resultiert der sog. Object-Relational-Mismatch 12

13 Der O/R-Mismatch Technische Ausprägungen des O/R-Mismatch:
Typen-Systeme Null Datum/Zeit Abbildung von Beziehungen Richtung Mehrfachbeziehungen Vererbung Identität Objekte haben eine implizite Identität Relationen haben eine explizite Identität (Primary Key) Transaktionen OO erlaubt die Konstruktion von neuen Typen (Klassen). DB: nur Strukturierung von primitiven Datentypen in Tupel und Sets Referenzen sind ein explizites Konstrukt in OO-Sprachen FKs sind kein explizites Konstrukt (Duplizierung von Schlüsselwerten) Referenzen sind gerichtet. Navigation entlang von Referenzen ist ein wichtiges Konzept. FKs sind nicht gerichtet. Man kann nicht entlang von FKs navigieren. Identität: In OO-Sprachen ist das Konzept der Identität tief verankert (this) Das Relationale Model kommt auch ohne Identität aus 13

14 Der O/R-Mismatch DB OO Designziele Speichern und Abfragen von Daten
Speicherung unabhängig von konkreten Business-Problemen Vereinigung von Zustand und Verhalten Kapselung, Modularisierung, Abstraktion Modellierung konkreter Business-Probleme Architektur-ansätze Client/Server: verteiltes System Objekte sind lokal und nicht verteilt Abfrage/Zugriff Deklarative Abfragesprache Beziehungen zwischen Daten müssen nicht explizit definiert sein Imperative Navigation entlang von Referenzen Beziehungen zwischen Objekten müssen explizit definiert sein Daten leben länger als Applikationen DB: Transformation / Migration / Reports über lange Zeit DB: SQL untypisierte Sicht auf die Daten OO: Streng typisierte Sicht auf die Daten 14

15 Der O/R-Mismatch In heutigen Enterprise Umfeldern ist der O/R-Mismatch ein Fakt. Der O/R-Mismatch folgt aus konzeptionellen Unterschieden der zugrundeliegenden Technologien. Es gibt viele verschiedene Möglichkeiten den O/R-Mismatch zu überwinden. O/R-Mapping resp. O/R-Mapping Frameworks sind ein möglicher Lösungsansatz. 15

16 History Konzepte zum Überbrücken des O/R-Mismatch existieren seit es OO gibt. Unterschiedliche Ansätze wie der O/R-Mismatch überwunden werden soll Bekannteste Java Frameworks Hibernate TopLink, TopLink Essentials, EclipseLink KODO, OpenJPA JPOX , DataNucleus Java Standardisierung: EJB2, JDO, JPA EJB2: 2001 JDO (JSR 12): 2002 JDO2 (JSR 243): 2006, JDO , JDO 2.3 in planning JPA (JSR 220): 2006 JPA 2 (JSR 317): 2009 16

17 Versprechen von O/R-Mapping
Die Applikation wird von der DB entkoppelt Applikationsentwickler muss kein SQL beherrschen Das relationale Modell der Datenbank hat keinen Einfluss auf das OO-Design Automatische Persistenz Automatisierte Abbildung der Objekte in die relationalen Strukturen Der Applikationsentwickler muss sich nicht um die “low-level”-Details des CLI (z.B. connections) kümmern. Transparente Persistenz / Persistence Ignorance Die Klassen des Domain-Models wissen nicht dass sie persistiert und geladen werden können und haben keine Abhängigkeit zur Persistenz-Infrastruktur. 17

18 Versprechen von O/R-Mapping
Abstraktion Abstraktion ist eines der wichtigsten Werkzeuge um Komplexität zu bewältigen Separation of Concerns Bei der Applikationsentwicklung kann man sich ausschliesslich auf die Geschäftsprobleme konzentrieren Infrastruktur-Probleme können separat gelöst werden und beeinflussen das Design und die Implementation der Geschäftslogik nicht. 18

19 Versprechen von O/R-Mapping Frameworks
Umsetzung der Versprechen von O/R-Mapping Einsatz von bewährten Patterns und Konzepten Einsparungen von viel Code Manuell codierter DataAccess-Layer kann einen grossen Code-Anteil ausmachen Referenzbeispiele zeigen Einsparungen um Faktor 7 Strukturierung / Layerung des Codes vorgegeben 19

20 Pitfalls von O/R-Mapping Frameworks
O/R-Mapping-Frameworks sind komplex und bieten viel Funktionalität. Hibernate Core: 765k LOC, 206 Personen-Jahre, $11 Mio (Source: Ohloh.net) O/R-Mapping-Frameworks sind keine Rapid-Application-Development-Tools Der Einsatz eines O/R-Mapping-Frameworks hat Einfluss auf die Architektur und den Design der gesamten Applikation Die zugrundeliegenden Konzepte sollten verstanden sein. Gründliche Auseinandersetzung ist Voraussetzung für den erfolgreichen Einsatz eines O/R-Mapping-Frameworks 20

21 Konzeptionelle Probleme beim O/R-Mapping
Folgende konzeptionelle Probleme sollten beim O/R-Mapping beachtet werden: Location Transparency Optimiertes SQL Mengen-Manipulationen Relationale Suche / Reports / OLAP 21

22 Location Transparencey
Alle Daten immer verfügbar Für die Applikation sollte es keinen Unterschied machen, ob die sich Daten im lokalen Speicher oder auf der entfernten Datenbank befinden. Wieviele Daten werden geladen? Zuviele: Speicher, Bandbreite -> Ladezeit! Zuwenige: Konstantes Nachladen -> Ladezeit! Stichworte: Lazy-Loading / Eager-Loading 22

23 Dynamisch Generiertes SQL
SQL ist nicht so performant wie handgeschriebens, getuntes SQL Assembler anybody? Dynamisch generiertes SQL muss auch nicht gewartet werden! Ausgereifte Frameworks generieren gut optimiertes SQL (torpedo-group.org) Performance Stored Procedures sind nicht performanter als dynamisches SQL! 23

24 Relationale Suche / Reports
Nutzung von relationalen Konzepten, die keine Entsprechung in der OO-Welt haben Unproblematische Abfragen: Alle Personen, welche einen schwarzen Mantel bestellen. Alle Personen, welche einen schwarzen Mantel bestellen mit all ihren Bestellungen und allen Bestell-Posten 24

25 Relationale Suche / Reports
Problematische Abfrage: Alle Tupel bestehend aus Vorname, Nachname und Produktname FirstName LastName ProductName Thomas Anderson Beretta 92FS 9mm Black Coat Cool Sunglasses Tyler Durden Perfumed Soap 25

26 Relationale Suche / Reports
Probleme: Karthesisches Produkt In der DB sehr effizient umgesetzt In der OO-Welt keine Entsprechung Die Struktur des Resultats der Anfrage existiert nicht in der OO-Welt Es gibt keinen Typ mit den Attributen (Vorname, Nachname, Produktname) Die relationale Welt erlaubt flexible (untypisierte) Sichten auf die Daten (Deklarative ad-hoc formulierte Anfragen) Resultat einer Anfrage kann völlig entkoppelt sein von der Struktur wie die Daten gespeichert werden. Die OO-Welt verlangt definierte, stark typisierte Strukturen Das ‚Flachdrücken‘ / ‚Denormalisieren‘ muss in der OO-Welt manuell ausgeführt werden Alle beteiligten Objekte müssen geladen werden (Performance!) 26

27 JPA – Java Persistence API
JPA ist eine Bestrebung OR-Mapping mit Java zu standardisieren JPA ist ein Teil der EJB 3 Spezifikation welche vom JCP erarbeitet wurde JSR 220, final release: TopLink Essentials ist die Referenzimplementation Es gibt verschiedene JPA Implementationen, diese werden auch JPA Providers genannt JPA 2 ist eine eigenständge Spezifikation, welche auf JPA aufbaut und etliche zusätzliche Features bietet JSR 317, final release: EclipseLink ist die Referenzimplementation Die bekannten JPA Providers unterstützen alle JPA2 27

28 Technologiestack Session Beans / Java Applikation Java 5+
Java Persistence API JPA Spezifikation Java Persistence API Implementation EclipseLink (TopLink), Hibernate, OpenJPA etc. JDBC API JDBC 4.0 JDBC - Driver Herstellerspezifisch SQL SQL 2003 oder Dialekte RDB 28

29 Java API Runtime Exceptions ( ~8 ) RollbackException
Annotations ( ~64 ) Entity Id OneToOne OneToMany Enumerations ( ~10 ) InheritanceType CascadeType FetchType Packages javax.persistence javax.persistence.spi Classes Persistence Interfaces EntityManagerFactory EntityManager EntityTransaction Query 29

30 JPA Providers Die bekanntesten JPA Providers sind:
Hibernate Toplink / TopLink Essentials / EclipseLink KODO / OpenJPA JPOX / DataNucleus Weniger bekannt sind: Apache Cayenne Resin Amber CocoBase 30

31 Objektrelationales Mapping mit JPA 2.0
Getting Started Objektrelationales Mapping mit JPA 2.0 31

32 Entity Überblick Eine Entity ist persistierbar. Der Zustand kann in einer Datenbank abgespeichert und später wieder darauf zugegriffen werden Wie jedes andere Java Objekt hat eine Entity eine Objektidentität. Zusätzlich besitzt sie eine Datenbankidentität (Primary Key) In Zusammenhang mit der Datenbank werden die Entities transaktional verwendet. Die Erstellung, Änderung und das Löschen wird in einer Transaktion durchgeführt

33 Entity Metadata Kennzeichnung mit oder Mapping mit XML Klasse kann Basisklasse oder abgeleitet sein Klasse kann abstrakt oder konkret sein Serialisierbarkeit ist bezüglich Persistenz nicht erforderlich Anforderungen: Standardkonstrukutor muss vorhanden sein. Klasse darf nicht final, kein Interface und keine Enumeration sein und keine final-Methoden enthalten Felder müssen private oder protected sein. Zugriff von Clients auf Felder nur über get/set- oder Business-Methoden erlaubt. Jede Entity muss einen Primärschlüssel haben Configuration by Exception / Conventions over Configuration

34 Entity, Beispiel @Entity public class Employee { @Id protected int id;
protected String name; protected long salary; public Employee(int id) { } ...

35 Entity Manager, Überblick

36 Entity Manager, Beispiel
Entity Manager erstellen Entity persistieren Entitiy suchen EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa.emp.2008"); EntityManager em = emf.createEntityManager(); Employee emp = new Employee(158); em.persist(emp); Employee emp = em.find(Employee.class, 158);

37 Entity Manager, Beispiel
Entity verändern Entity löschen Queries em.getTransaction().begin(); emp.setSalary(emp.getSalary() ); em.getTransaction().commit(); em.getTransaction().begin(); em.remove(emp); em.getTransaction().commit(); Query q = em.createQuery("SELECT e FROM Employee e"); Collection emps = q.getResultList();

38 Persitence Unit Eine Persistence Unit ist eine logische Einheit von Entities. Sie wird beschrieben durch: Einen Namen Die zu dieser Unit gehörenden Entity-Klassen Angaben zum Persistence Provider Angabe zum Transaktionstyp Angaben zur Datenquelle Weitere Properties Namen von XML O/R-Mapping Files Technisch wird die Beschreibung einer Persistence Unit in der Datei META-INF/persistence.xml abgelegt. Persitence Archive = JAR

39 Persistence Unit, Beispiel
META-INF/persistence.xml <persistence> <persistence-unit name="emp" transaction-type="RESOURCE_LOCAL"> <provider>oracle.toplink.essentials.PersistenceProvider</provider> <class>examples.model.Employee</class> <properties> <property name="toplink.jdbc.driver" value="oracle.jdbc.driver.OracleDriver" /> <property name="toplink.jdbc.url" /> <property name="toplink.jdbc.user" value="emp3" /> <property name="toplink.jdbc.password" value="emp3" /> </properties> </persistence-unit> </persistence>

40 Objektrelationales Mapping mit JPA 2.0
Entity Mapping Objektrelationales Mapping mit JPA 2.0 40

41 Access Typ Für das Persistenz-Framework existieren zwei Zugriffspunkte auf die Daten einer Klasse // Field public class Employee { @Id private int id; } //Property public class Employee { protected int id; @Id public int getId() { return id; } }

42 Access Typ Options (JPA 2.0)
Verschiedene Access Types pro Klasse möglich Mischen von Acces Types in einer Vererbungshierarchie public class Vehicle { ... @Transient double fuelEfficiency; @Access(PROPERTY) protected double getDbFuelEfficiency() { return convertToImperial(fuelEfficiency); } ...

43 Mapping Es wird immer vom Defaultverhalten ausgegangen
Das Defaultverhalten kann übersteuert werden = "EMP") public class Employee { @Id @Column(name = "EMP_ID") }

44 Persistente Datentypen
Erlaubt: Alle primitiven Typen, String Alle Wrapperklassen und serialisierbaren Typen (z.B. Integer, BigDecimal, Date, Calendar) byte[], Byte[], char[], Character[] Enumerations Beliebige weitere Entity-Klassen Collections von Entities, welche als Collection<>, List<>, Set<> oder Map<> deklariert sind. Nicht erlaubt: Alle Arten von Arrays ausser die obgenannten Collections von etwas anderem als Entities, also z.B. Wrapperklassen und andere serialiserbare Typen.

45 Java / SQL Type Mapping Implizit durch JDBC “Data Type Conversion Table“ definiert Explizit durch Annotation, z.B. Produktspezifisch durch JPA-Implementation oder im JDBC-Driver für die jeweilige Datenbank @Column(name = "sender") protected String sender;

46 Lazy Fetching und Large Objects
Lazy Field Loading Large Objects @Basic(fetch = FetchType.LAZY) private String comments; @Basic(fetch = private byte[] picture;

47 Enumerations Enumerations können persistent sein. In der Datenbank wird entweder der Ordinalwert (Position) oder der Stringwert (Name der Konstante) abgelegt. Vorsicht bei Änderungen an der Enumeration // Variante protected MessageStatus status; // Variante protected MessageStatus status;

48 Temporale Typen Erlaubte Zeittypen:
java.sql.Date, java.sql.Time, java.sql.Timestamp java.util.Date, java.util.Calendar java.sql Typen brauchen keine weitere Definition Bei java.util Typen muss der JDBC Typ angegeben werden: Mögliche Typen: TemporalType.DATE, TemporalType.TIME, TemporalType.TIMESTAMP @Temporal(TemporalType.DATE) private Calendar dob;

49 Transiente Attribute Attribute können von der Persistierung ausgeschlossen werden Entweder mittels transient: oder wenn das Attribut serialisiert werden soll mittels Annotation: transient private String translatedName; @Transient private String translatedName;

50 Entity Identity - Primärschlüssel
Jede Entity-Klasse muss einen bezeichneten Primärschlüssel besitzen. Eine Id kann von folgenden Typen sein: Primitive Java Typen: byte, int, short, long, char Wrapper Klassen: Byte, Integer, Short, Long, Character Array von primitiven Typen oder Wrapper Klassen java.lang.String java.math.BigInteger Zeittypen: java.util.Date, java.sql.Date Floating Point Typen sind ebenfalls erlaubt, aber sind aufgrund der möglichen Rundungsfehler nicht zu empfehlen

51 Primärschlüssel Generierung
Primärschlüssel können in Zusammenarbeit mit der Datenbank generiert werden. Beispiel: Strategien sind Identity, Table, Sequence und Auto @Entity public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Integer id; }

52 GenerationType.TABLE public class Employee { @TableGenerator(name = "Emp_Gen", table = "ID_GEN", pkColumnName = "GEN_NAME", valueColumnName = "GEN_VAL") @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "Emp_Gen") private int id; }

53 Objektrelationales Mapping mit JPA 2.0
Beziehungen Objektrelationales Mapping mit JPA 2.0

54 Eine Parent-Child Beziehung
Wie sieht das Klassen-Modell aus? Employee hat eine Referenz auf Department Department hat eine Collection von Employee Referenzen Beides: Bidirektionale Beziehung Unabhängig davon ist das zugrundeliegende DB-Schema:

55 Eine Parent-Child Beziehung
Mapping des Klassenmodells auf das DB-Schema mittels JPA: Metadata ist erforderlich. Je nach Klassenmodell wird entweder eine many-to-one Beziehung oder eine one-to-many Beziehung gemappt Falls beide Richtungen gemappt werden sollen, so muss definiert werden, dass für beide derselbe Foreign-Key zugrunde liegt.

56 Eine Parent-Child Beziehung
@Entity public class Employee { @ManyToOne private Department department; Mapping der many-to-one Beziehung Mapping der one-to-many Beziehung Field/Property muss ein Interface sein Achtung: Unidirektionales one-to-many ohne Beziehungstabelle wird erst ab JPA2 unterstützt @Entity public class Department { @OneToMany @JoinColumn (name=”department_id”) private Set<Employee> employees = new HashSet<Employee>(); @Entity public class Department { @OneToMany (mappedBy = “department“) private Set<Employee> employees = new HashSet<Employee>(); Mapping der bidirektionalen Beziehung JPA muss wissen, dass nur ein Foreign-Key für beide Richtungen existiert.

57 Collection Types Richtung Kardinalität Unidirektional Bidirektional
One-to-one Many-to-one One-to-many many-to-many Source Target Employee Address Employee Project 1 1 Employee Address * 1 Employee Department 1 * Employee Phone * * Employee Project

58 one-to-one, unidirektional
Employee @OneToOne private Address address; entspricht: @JoinColumn(name="address_id", referencedColumnName = "id") (JPA2 unterstützt auch one-to-one mit einer zusätzlichen Zwischentabelle)

59 many-to-one, unidirektional
Employee @ManyToOne private Department department; (JPA2 unterstützt auch many-to-one mit einer zusätzlichen Zwischentabelle)

60 one-to-many, bidirektional
Phone @ManyToOne(optional = false) private Employee employee; Employee @OneToMany(mappedBy = "employee") private Collection<Phone> phones;

61 many-to-many, bidirektional
Employee @ManyToMany(mappedBy = "employees") private Collection<Project> projects; Project @ManyToMany private Collection<Employee> employees;

62 Many-To-Many Beziehungen
„If you think that two objects share a simple many-to-many relationship, you haven't looked closely enough at the domain. There is a third object waiting to be discovered with attributes and a life cycle all its own.“ - Dierk König Oft sind weitere Daten auf der Zwischentabelle nötig Üblicherweise mappt man dann die Zwischentabelle auf eine eigene Entity

63 one-to-many, unidirektional
Bei einer unidirektionalen one-to-many Beziehungen fehlt das mappedBy Element und das Target hat keine Rückbeziehung JPA verwendet in diesen Fällen ebenfalls eine Beziehungstabelle @OneToMany private Set<Employee> employees = new HashSet<Employee>(); JPA 2 spezifiziert die unidirektionale one-to-many Beziehung ohne Zwischentabelle. @OneToMany @JoinColumn (name=”department_id”) private Set<Employee> employees = new HashSet<Employee>();

64 Bidirektionale Beziehungen
JPA verändert die Java-Semantik nicht! D.h. der korrekte Unterhalt von bidirektionalen Beziehungen ist Sache der Applikation! Department taxes = new Department(); Employee john = new Employee(); taxes.getEmployees().add(john); john.setDepartment(taxes);

65 Bidirektionale Beziehungen
Best Practice: Convenience Methoden auf den Entities: @Entity public class Department { @OneToMany private List<Employee> employees = new ArrayList<Employee>(); public void addEmployee(Employee employee){ if (employee == null) throw new IllegalArgumentException(“Null employee“); if (employee.getDepartment() != null) employee.getDepartment().getEmployees().remove(employee); getEmployees().add(employee); employee.setDepartment(this); } Analog: removeEmployee() sowie Methoden auf Employee.

66 Verwendung von Collections
java.util.Set Eindeutig (Object.equals()) @OneToMany private Set<Phone> phones; java.util.List geordnet, kann sortiert werden @OneToMany @OrderBy("phonenumber ASC") private List<Phone> phones; java.util.Map Key/Value Paare @OneToMany @MapKey(name = "phonenumber") private Map<String, Phone> phones; JPA 2: Persistenter Index @OneToMany @OrderColumn(name="index") private List<Phone> phones;

67 Lazy- und Eager-Loading
Beziehungen werden transparent (nach)geladen: EntityManager em = ... Department sales = em.find(Department.class, 123); sales.getEmployees().iterator().next(); Default bei one-to-one und many-to-one FetchType.EAGER Default bei one-to-many und many-to-many FetchType.LAZY Defaultverhalten kann übersteuert werden. z.B. @OneToMany(fetch = FetchType.EAGER) private Set<Phone> phones;

68 Speichern und Löschen von Beziehungen
Department taxes = new Department(); Employee john = new Employee(); taxes.addEmployee(john); Employee jane = new Employee(); taxes.addEmployee(jane); em.persist(taxes); em.persist(john); em.persist(jane); em.flush(); Jede Entity hat einen eigenen, unabhängigen Lifecycle! IllegalStateException wenn vergessen wird, eine assoziierte Entity zu persistieren. for (Employee empl : taxes.getEmployees()){ em.remove(empl); } em.remove(taxes); em.flush(); It is still possible to violate a NOT NULL constraint on a foreign key column by deleting objects in the wrong order, e.g. if you delete the parent, but forget to delete the children. Delete all entities individually

69 Transitive Persistenz
Persistenz wird von JPA propagiert auf assoziierte Entities. CascadeType {ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH}; @OneToMany (mappedBy = “department“, cascade = CascadeType.ALL) private Set<Employee> employees = new HashSet<Employee>(); Kaskadierung wird auf der Assoziation konfiguriert Department taxes = new Department(); Employee john = new Employee(); taxes.addEmployee(john); Employee jane = new Employee(); taxes.addEmployee(jane); em.persist(taxes); em.flush(); Speichern eines Parents speichert auch alle Kinder em.delete(taxes); em.flush(); Löschen eines Parents löscht auch alle Kinder.

70 Orphan Deletion Child wird nicht gelöscht!
Phone phone1 = … Employee john = em.find(Employee.class, 123); john.getPhones().remove(phone1); em.flush(); Child wird nicht gelöscht! Entfernen eines Kindes aus der Collection des Parents setzt nur den Foreign Key auf der Kind-Tabelle auf NULL. FK Constraint Verletzung möglich Das Kind ist nun “orphaned” In JPA 1 muss das Löschen von Orphans explizit in der Applikation erfolgen. JPA 2 unterstützt das automatische Löschen von Orphans @OneToMany(cascade=ALL, mappedBy=”customer”, orphanRemoval=true) public Set<Order> getOrders() { return orders; }

71 Working With Persistent Objects
Objektrelationales Mapping mit JPA 2.0 71

72 Persistence Context Der Persistence Context definiert das physische Umfeld von Entities zur Laufzeit: die Menge aller Managed Entities in der Applikation den Entity Manager für diese Entities die laufende Transaktion den Contexttyp

73 Kontext Typen TRANSACTION EXTENDED Standard im Java EE Umfeld
Lesender und schreibender Zugriff nur innerhalb der Transaktion. Gelesene Objekte sind nach der Transaktion im Zustand detached Wiedereinkopplung in eine Transaktion mit merge() EXTENDED Standard im Java SE Umfeld Alle Objekte sind lesend und schreibend zugreifbar Modifikationen finden lokal statt Effekt von persist(), remove() usw. wird aufbewahrt Propagation von Efffekten und Änderungen in die DB aber nur, wenn nachträglich begin()/commit() ausgeführt wird

74 Objektverwaltung Der Transfer von Objekten von und zur Datenbank erfolgt automatisch: so spät wie möglich  Lazy Access Der Transfer von Objekten von und zur Datenbank kann manuell erzwungen werden  synchron zum Aufruf Selbstverständlich gilt ein Transaktionsmodell: Der Zugriff auf Objekte erfolgt ab Beginn der Transaktion, die Synchronisation mit der Datenbank wird spätestens beim Commit abgeschlossen und unterliegt der ACID-Regel Auf Objekte kann auch ausserhalb von Transaktionen zugegriffen werden, jedoch ohne Konsistenz- und Synchronisationsgarantie

75 Objektzustand Objekte haben vier mögliche Zustände: New Managed
Objekt ist neu erzeugt, hat noch keinen Zusammenhang mit der Datenbank und noch keine gültige ID. Managed Das Objekt hat eine Entsprechung in der Datenbank. Änderungen werden vom Entity Manager automatisch getracked und mit der DB abgeglichen. Detached Das Objekt hat eine Entsprechung in der Datenbank, wurde aber abgekoppelt. Der Zustand wird nicht mehr automatisch abgeglichen mit der Datenbank. Removed Das Objekt existiert noch, ist aber zum Löschen markiert.

76 Zustände und Übergänge

77 Entity persistieren Mit persist() wird eine neue Entity vom EntityManager verwaltet Die Methode contains() kann geprüft werden ob eine Entity managed ist Department dept = em.find(Department.class, deptId); Employee emp = new Employee(); emp.setId(empId); emp.setName(empName); emp.setDepartment(dept); dept.getEmployees().add(emp); em.persist(emp);

78 Objekt ID Ein neues Objekt bekommt erst eine ID, wenn es das erste Mal physisch in die Datenbank transportiert wird Employee emp = new Employee(); em.persist(emp); System.out.println(emp.getId()); // null em.flush(); oder em.getTransaction().commit(); System.out.println(emp.getId()); // gültige ID, z.B. 1 TODO: Jonas -> Ich glaube das stimmt nicht… Spez. Kap. 3.2

79 Kaskadierte Persistenz (1)
Kaskadierte Persistenz heisst: Alle von einem persistenten Objekt aus erreichbaren Objekte sind ebenfalls persistent Die Kaskadierung muss deklariert werden: PERSIST MERGE REMOVE REFRESH ALL Employee employee = new Employee(); em.persist(emp); Address address = new Address(); employee.setAddress(address);

80 Kaskadierte Persistenz (2)
Die Kaskadierung kann für das Erstellen und das Löschen der Persistenz separat eingestellt werden Die Kaskadierung bezieht sich nun auf die persist() und die remove()-Methode. public class Employee { @OneToOne( cascade={CascadeType.PERSIST, CascadeType.REMOVE } ) private Address address; ... }

81 Orphan Removal (JPA 2.0) Sollen abhängige Kindelemente bei to-many Beziehungen ebenfalls gelöscht werden, kann dies seit JPA 2.0 ebenfalls deklariert werden: @OneToMany(cascade=ALL, mappedBy=”customer”, orphanRemoval=true) private Set<Order> orders;

82 Entity suchen Mit find() kann eine Entity über ihren Primary Key gefunden werden Die gefunden Entity wird kommt automatisch in den Zustand managed Da find() über den Primary Key sucht, kann diese Methode vom Persistence Provider optimiert werden und unter Umständen einen Datenbankzugriff vermieden werden Soll eine one-to-one oder many-to-one Reference auf eine bestehende Entity gebildet werden, kann getReference() verwendet werden um das vollständige Laden der Target-Entity zu verhindern

83 Einlesen Der Objektzustand wird beim ersten Zugriff auf das Objekt eingelesen. Wenn FetchType.EAGER gesetzt ist, werden referenzierte Objekte ebenfalls mitgeladen. Wenn FetchType.LAZY gesetzt ist, werden referenzierte Objekte beim ersten Gebrauch eingelesen. Der Objektzustand wird nie automatisch aufgefrischt, nur via die EntityManager.refresh()-Methode. Eine neue Transaktion führt nicht automatisch zum erneuten Einlesen bestehender Objekte.

84 Entity löschen Mit remove() wird dem EntityManager mitgeteilt, dass diese Entity gelöscht werden kann Employee emp = em.find(Employee.class, empId); em.remove(emp);

85 Objektzustand nach Commit
Wenn der Persistence Context EXTENDED ist, bleibt ein Objekt im Zustand managed nach dem Commit. Änderungen nach dem Commit werden aufbewahrt und im Rahmen der nächsten Transaktion in die Datenbank übernommen. Wenn der Persistence Context TRANSACTION ist, geht ein Objekt in den Zustand detached über nach dem Commit. Änderungen müssen mit EntityManager.merge() innerhalb der nächsten Transaktion wieder eingekoppelt werden.

86 Objektzustand nach Rollback
Nach einem Rollback ist jedes noch vorhandene Objekt im Zustand detached. Die Belegung der Felder wird durch den Rollback nicht geändert, jedoch der Zustand in der Datenbank Vorsicht vor möglichen Inkonsistenzen Objekte via Methoden des Entity Manager neu laden: find(), getReference(), createQuery() usw.

87 Persistence Context aufräumen
Ab und zu kann es vorkommen, dass der Persistence Context gelöscht werden soll Dies kann mit der Methode clear() des EntityManager erreicht werden Alle Entities kommen in den Zustand detached Vorsicht: enthält der Persistence Context Änderungen welche noch nicht mit commit() gespeichert wurden, gehen diese verloren

88 Synchronisation mit der Datenbank
Das Zurückschreiben findet zum Commit-Zeitpunkt oder explizit mit der flush() Methode statt. Das Zurückschreiben beinhaltet kein Refresh allfälliger Änderungen in der Datenbank in der Zwischenzeit -> Lost Update! Das Zurückschreiben betrifft nur Änderungen, nicht ungeänderte Daten. Applikation m.setContent("A") tx.commit() m.setContent("C") Zustand von m in Appl in DB A A A B C C SQL-Client update Message set content = 'B'; commit

89 Foreign Key Constraints
Änderungsoperationen erzeugen oft Konflikte mit Fremdschlüssel-bedingungen, wenn Datensätze in der falschen Reihenfolge gelöscht werden. Oracle erlaubt die Constraints DEFERABLE zu definieren, damit diese erst zum Commit Zeitpunkt geprüft werden: ALTER TABLE EMPLOYEE ADD CONSTRAINT EMPLOYEE_ADDRESS_FK FOREIGN KEY (ADDRESS_ID) REFERENCES ADDRESS(ID) DEFERRABLE INITIALLY DEFERRED;

90 Objektrelationales Mapping mit JPA 2.0
Querying Objektrelationales Mapping mit JPA 2.0

91 Entities laden In JPA gibt es verschiedene Optionen Entitäten zu laden: Eine einzelne Instanz über die ID laden Navigation auf dem Objektgraphen Queries in der Java Persistence Query Language (JPQL) Queries in SQL In JPA2 kommt neu die Criteria API hinzu

92 Entity über die ID laden
Der EntityManager stellt zwei Möglichkeiten zur Verfügung: find() und getReference() EntityManager em = … Integer id = 1234; Employee john = em.find(Employee.class, id); Falls die Entität bereits im Persistence Context geladen ist, so wird kein DB-Query ausgeführt. find(): Wenn sich die Entity noch nicht im Persistence Context befindet, so wird sie von der DB geladen. Resultat is null, falls die Entity nicht existiert getReference(): Wenn sich die Entität noch nicht im Persistence Context befindet, so wird ein Proxy zurückgegeben. Es wird vorerst kein DB-Query ausgeführt. Dieses erfolgt erst wenn auf die Entität zugegriffen wird. EntityNotFoundException erst beim Zugriff, falls die Entity nicht existiert.

93 Navigation des Objektgraphen
Ausgehend von einer Entity kann ein Objektgraph traversiert werden. Dabei lätdt JPA transparent alle notwendigen Daten von der DB. Dieses Feature wird “Lazy Loading” genannt Die Entities müssen persistent und der EntityManager muss offen sein Dies ist ein mächtiges Feature, birgt aber auch Gefahren

94 Queries mit JPQL JPQL ist eine mächtige Abfragesprache basierend auf dem Entitätenmodell: Stark an SQL angelehnt Unabhängig von der darunterliegenden Datenbank Abfragen basieren auf dem Klassenmodell (Entitäten), nicht auf dem Datenmodell (Tabellen) Unterstützt OO-Konstrukte wie Vererbung, Polymorphismus und Pfadausdrücke String queryString = “select e.address from Employee e where e.mainProject.name = ‘JPA Kurs‘“; Query query = em.createQuery(queryString); List<Address> users = query.getResultList();

95 Verwendung von JPQL Typischerweise wird JPQL verwendet um Entities zu laden. JPQL unterstützt aber auch andere Szenarien: Abfrage von skalaren Werten (Projektionen oder Aggregationen) Bulk Updates und Deletes Reporting Queries: Rückgabe von Daten-Tupels, Nutzung von Gruppierungs- und Aggregationsfunktionen der DB Constructor Expressions: Abfüllen von beliebigen Objekten (nicht notwendigerweise Entities) JPQL kann entweder in Dynamischen Queries oder in Named Queries verwendet werden.

96 Dynamische Queries Bei Dynamischen Queries wird der JPQL String zur Laufzeit erstellt. Kontextabhängige Queries String Concatenation EntityManager em = ... String queryString = “select e from Employee e where e.address.city = ‘Bern‘“; Query query = em.createQuery(queryString); List<Employee> employees = query.getResultList();

97 Named Queries Named Queries werden statisch definiert und können überall in der Applikation verwendet werden. Die JPA Infrastruktur kann Named Queries vor der eigentlichen Ausführung parsen und kompilieren (Prepared Statements) Parsen/Kompilierung muss nur einmal durchgeführt werden Kann beim Deployen/Startup erfolgen und überprüft werden (Fail Fast) @NamedQuery(name = "Employee.findAll", query = "SELECT e FROM Employee e") public class Employee { ... } EntityManager em = ... Query q = em.createNamedQuery("Employee.findAll"); List<Employee> employees = query.getResultList(); Named Query kann bei allen annotierten Klassen angefügt werden Name muss unique sein innerhalb der Persistence Unit Tip: Anstatt Strings Konstanten verwneden

98 Parameter Binding Queries sollten parametrisert werden. Es gibt zwei Arten der Parametrisierung: Named Parameters Positional Parameters SELECT e FROM Employee e WHERE e.department = :dept AND e.salary > :base Query q = ... q.setParameter("dept", "Taxes"); q.setParameter("base", "3500"); SELECT e FROM Employee e WHERE e.department = ?1 AND e.salary > ?2 Query q = ... q.setParameter(1, "Taxes"); q.setParameter(2, "3500");

99 Queries ausführen Abholen des Resultates mit Methoden von Query List getResultList() Object getSingleResult() int executeUpdate() Beispiel Query q = em.createQuery("SELECT e FROM Employee e"); List<Employee> emps = q.getResultList(); for(Employee e : emps) { System.out.println(e); } NonUniqueResultException wenn mehrere Resultate bei getSingleResult()

100 JPQL Sprach-Features JPQL ist eine sehr mächtige und flexible Abfragesprache. Hier nur einige Features: JOINS und Subqueries (IN, EXISTS) Aggregatsfunktionen (AVG, COUNT, MIN, MAX, SUM) GROUP BY und HAVING Funktionen (LOWER, ABS, TRIM ...) LIKE Collection-Abfragen: IS EMPTY, MEMBER ANY, ALL, SOME,

101 Pfad-Ausdrücke Ein Pfadausdruck ermöglicht die direkte Navigation von einem äusseren zu inneren, referenzierten Objekten: SELECT e.address FROM Employee e SELECT e.address.name FROM Employee e Ein Pfadausdruck kann in einer Collection enden: SELECT e.projects FROM Employee e Ein Pfadausdruck kann nicht über eine Collection hinweg navigieren: SELECT e.projects.name FROM Employee e

102 Pagination Mit JPA ist Pagination sehr einfach:
String queryString = “select e from Employee“; Query query = em.createQuery(queryString); query.setFirstResult(110); query.setMaxResults(10); List<Order> orders = query.getResultList(); JPA schreibt nicht vor, wie Pagination umgesetzt wird! Dies kann von JPA-Implementation und DB-Dialekt abhängen. In der Regel wird das resultierende SQL-Query für den entsprechenden SQL-Dialekt optimiert. Achtung: Meist wird das SQL-Rowset limitiert, und nicht die resultierenden Entities!

103 Fetching & Lazy Loading
Die Idee von Lazy Loading ist es, die Daten erst dann von der DB zu laden, wenn sie auch wirklich in der Applikation benötigt werden. Das Laden sollte für den Client transparent sein Dem Programmierer wird viel Arbeit erspart Nachteile: Traversieren eines Objekt-Graphen kann in vielen einzelnen DB-Queries resultieren “N+1 select problem”: Für eine Parent-Child Beziehung wird für jedes Kind ein eigenes DB-Query abgesetzt Das Gegenteil von Lazy Loading ist Eager Loading

104 Fetching & Lazy Loading
In JPA kann das Lade-Verhalten auf zwei Weisen beeinflusst werden: Global Fetch Plan: Konfiguriert in den Entity-Metadaten (Annotationen/XML) Programmatisch beim Erstellen eines Queries mittels Join Fetch @OneToMany(mappedBy = "employee", fetch = FetchType.EAGER) private Set<Phone> phones = new HashSet<Phone>(); SELECT d FROM Department d LEFT JOIN FETCH d.employees Fetch Join analog mit JPA 2 Criteria API.

105 Joins & Fetching Es gibt unterschiedliche Joins in JPQL:
Fetch Joins für Eager Loading SELECT d FROM Department d LEFT JOIN FETCH d.employees Explizite Joins für Selektion und Projektion SELECT employee FROM Employee employee JOIN employee.projects project WHERE project.name = 'Arcos' SELECT project FROM Employee employee JOIN employee.projects project WHERE employee.name = 'John' Implizite Joins aus Pfadausdrücken SELECT e FROM Employee e where e.address.city = 'Bern' SELECT e.address FROM Employee e where e.name = 'John'

106 Polymorphe Queries JPQL unterstützt Polymorphie:
Query q = em.createQuery("select p FROM Project p"); List<Project> projects = q.getResultList(); Selektion aufgrund einer Subklasse: SELECT employee FROM Employee employee JOIN employee.projects project, DesignProject dproject WHERE project = dproject AND dproject.innovationLevel > 2 JPA 1: Queries sind immer polymorph! JPA 2: Einschränkungen des Typs mittels Type-Expression möglich SELECT p FROM Project p WHERE TYPE(p) IN (DesignProject)

107 Reporting Queries Wird mehr als eine Expression in der SELECT Klausel verwendet, wird ein Object[]-Array zurückgegeben: List result = em.createQuery( "SELECT e.name, e.department.name " + "FROM Project p JOIN p.employees e " + "where p.name = "ZLD").getResultList(); for (Iterator i = result.iterator(); i.hasNext()) { Object[] values = (Object[])i.next(); System.out.println(values[0] + "," + values[1]); } Solche Queries werden typischerweise für Reporting verwendet Das Resultat sind keine Entities und wird nicht vom Persistence Context gemanagt!

108 Constructor Expressions
Mit Constructor Expressions existiert eine einfache Möglichkeit um Resultate auf Klassen zu mappen: public class EmployeeDTO { public String employeeName; public String deptName; public EmployeeTO(String employeeName, String deptName) {...} } List result = em.createQuery( "SELECT NEW jpa.util.EmployeeDTO(e.name, e.department.name) " + "FROM Project p JOIN p.employees e " + "where p.name = "ZLD").getResultList(); for (EmployeeTO emp : result) { System.out.println(emp.employeeName + "," + emp.deptName); } Achtung: Klasse muss vollqualifiziert angegeben werden! Kann auch mit Entities verwendet werden. Das Resultat wird nicht vom Persistence Kontext gemanagt.

109 Bulk Statements In JPQL können UPDATE und DELETE-Statements formuliert werden, welche auf eine Menge von Entities angewendet werden. Query q = em.createQuery("DELETE from Employee e"); int count = q.executeUpdate(); Query q = em.createQuery("UPDATE Employee e " "SET e.name = 'Simon' " "WHERE e.name = 'Peter'); int count = q.executeUpdate(); Achtung: Bulk Statements umgehen den Entity Manager! Damit geladene Entities die Veränderungen mitbekommen, müssen sie mit der Datenbank synchronisiert werden.

110 Vorteile und Nachteile von JPQL
Sehr mächtig und flexibel Stark an SQL angelehnt Nachteile JPQL ist eine embedded Language die in Java mittels Stings verwendet wird. Keine Überprüfung beim Kompilieren, keine Typ-Sicherheit Flexible Komposition eines Queries ist nicht elegant möglich (String-Manipulation) Für nicht-triviale Anwendungen ist SQL Knowhow und Verständnis des Datenmodels ist notwendig

111 SQL Queries JPA ermöglicht die Formulierung von SQL-Queries:
Query q = em.createNativeQuery("SELECT * FROM emp WHERE id = ?", Employee.class); q.setParameter(1, employeeId); List<Employee> employees = q.getResultList(); SQL-Queries könne auch als NamedQuery definiert werden: @NamedNativeQuery(name = "employeeReporting", query = "SELECT * FROM emp WHERE id = ?", resultClass = Employee.class) Ausführung analog Named Queries in JPQL Stored Procedures werden in JPA nicht unterstützt. Die meisten JPA-Implementationen bieten jedoch proprietäre Mechanismen zum Einbinden von Stored Procedures.

112 SQL Queries Update und Delete Statements:
Query q = em.createNativeQuery("UPDATE emp SET salary = salary + 1"); int updated = q.executeUpdate(); Flexibles Mapping des Result-Sets @SqlResultSetMapping( name = "EmployeeWithAddress", entities = = Employee.class), @EntityResult(entityClass = Address.class)} String s = "SELECT e.*, a.* FROM emp e, address a" "WHERE e.adress_id = a.id"; Query q = em.createNativeQuery(s, "EmployeeWithAddress"); List<Employee> employees = q.getResultList();

113 Criteria API in JPA 2 Mit der Criteria API wird in JPA 2 eine Objekt-Orientierte Schnittstelle zum programmatischen Erstellen von Queries standardisiert. Vorteile: Dynamisches Erstellen von Queries (Komposition) Keine String-Manipulation notwendig OO-Konstrukte zum Erstellen komplexer Queries Gewisse Typsicherheit

114 Criteria API in JPA 2 Beispiel mit Strings für Referenzen:
QueryBuilder qb = ... CriteriaQuery q = qb.create(); Root<Customer> cust = q.from(Customer.class); Join<Order, Item> item = cust.join("orders").join("lineitems"); q.select(cust.get("name")).where( qb.equal(item.get("product").get("productType"), "printer")); Beispiel mit dem typsicheren, statischen Metamodel: QueryBuilder qb = ... CriteriaQuery q = qb.create(); Root<Customer> cust = q.from(Customer.class); Join<Customer, Order> order = cust.join(Customer_.orders); Join<Order, Item> item = order.join(Order_.lineitems); q.select(cust.get(Customer_.name)) .where(qb.equal(item.get(Item_.product).get(Product_.productType), "printer"));

115 Metamodel API Metamodel: statische Klassen, welche das Entity-Model abbilden Queries mit der Criteria API kann können mit dem Metamodel komplett typsicher programmiert werden @StaticMetamodel(Department.class) public class Department_ { public static volatile SingularAttribute<Department, Integer> id; public static volatile SingularAttribute<Department, String> name; public static volatile SetAttribute<Department, Employee> employees; }

116 Metamodel API Erstellung des Metamodels ist nicht standardisiert. Typischerweise erfolgt sie über einen JDK6 Annotation Processor (APT) compile Java Compiler Department.class Department.java Department_.class compile call Department_.java generate Annotation Processor

117 Objektrelationales Mapping mit JPA 2.0
Advanced Mapping Objektrelationales Mapping mit JPA 2.0 117

118 Embedded Objects Komposition: Mutterobjekt mit eingebetteten Objekten
Eingebettete Objekte haben keine eigene Identität Mutterobjekt und eingebettete sind in derselben Tabelle abgelegt

119 Embedded Objects, Beispiel
@Embeddable public class Address { private String street; private String city; private String state; private String zip; } @Entity public class Employee { @Id private int id; private String name; private long salary; @Embedded private Address address;

120 Enhanced Embeddables (JPA 2.0)
Verschachtelung von Embeddables und Embeddables mit Beziehungen @Embeddable public class Assembly { ... @Embedded ShippingDetail shipDetails; @ManyToOne Supplier supplier; }

121 Zusammengesetzte Primärschlüssel
public class EmployeeId implements Serializable { protected String country; protected int id; } // Variante 1 @IdClass( EmployeeId.class public class Employee { @Id protected String country; @Id protected int id; // Variante 2 @Entity public class Employee { @EmbeddedId public EmployeeId id;

122 Composite Primary Key With Relationships (JPA 2.0)
public class Part { @Id int partNo; Supplier supplier; } public class PartPK { int partNo; int supplier;

123 Read-Only Mappings und Optional
= false, updateable = false) = true) // Default

124 Mehrere Tabellen pro Entity
Gelegentlich wird eine Entity in der Datenbank in mehrere Tabellen modelliert oder muss umgekehrt aus mehreren Tabellen zusammengesetzt werden. Eine Entity kann beliebig viele Sekundärtabellen haben: @Entity @Table( name = "Employee" ) @SecondaryTable( name = "EmployeeExt", pkJoinColumns = { @PrimaryKeyJoinColumn(name="idRef", referencedColumnName = "id")}) public class Employee { }

125 Mapping von SQL-Views Komplexe Abfragen werden in einer Datenbank gerne als (materialisierte) View vorgehalten. Um Performance-Vorteile zu nutzen, kann eine View wie eine Entity angesprochen werden. Beispiel: @Entity public class EmployeeStats{ @Id protected long id; @Column( insertable=false, updatable=false) protected int numPhones; // ... CREATE VIEW employeestats ( id, numPhones ) AS SELECT e.id, COUNT(p.*) FROM employee e LEFT OUTER JOIN PHONES P on (…) group by e.id

126 Vererbung Vererbungshierarchien können problemlos verwendet und abgebildet werden. Klassen können abstrakt oder konkret sein. Alle Klassen in der Vererbungshierarchie müssen den Primärschlüssel der Basisklasse verwenden (erben). Es gibt vier Mappingstrategien auf die Datenbank: Eine einzige Tabelle für die gesamte Verbungshierarchie Eine Tabelle für jede konkrete Klasse Eine Tabelle für jede Klasse Mapped Superclass

127 SINGLE_TABLE @Entity @Inheritance public class Project
@Entity public class DesignProject extends Project @Entity public class QualityProject extends Project

128 JOINED @Entity @Inheritance(strategy=InheritanceType.JOINED)
public class Project @Entity public class DesignProject extends Project @Entity public class QualityProject extends Project

129 TABLE_PER_CLASS (Optional)
public class Project @Entity public class DesignProject extends Project @Entity public class QualityProject extends Project

130 MAPPED_SUPERCLASS // Klasse BaseEntity @MappedSuperclass
public abstract class BaseEntity { protected Integer id; protected Integer version; protected Timestamp createdAt; protected String createdFrom; protected Timestamp updatedAt; protected String updatedFrom; // Klasse Phone @Entity public class Phone extends BaseEntity

131 Objektrelationales Mapping mit JPA 2.0
Performance Objektrelationales Mapping mit JPA 2.0

132 “You can't control what you can't measure.” - Tom DeMarco
“Premature Optimization is the Root of all Evil.” - Donald Knuth “You can't control what you can't measure.” - Tom DeMarco

133 SQL Logging Logging des JPA-Providers
z.B: in persistence.xml: Logging/Profiling Funktionalität der DB Logging/Profiling auf JDBC Ebene Log4jdbc (http://code.google.com/p/log4jdbc/) Log4jdbc-remix (http://code.google.com/p/log4jdbc-remix/) P6Spy (http://www.p6spy.com/) <property name="eclipselink.logging.level.sql" value="FINE"/> <property name="eclipselink.logging.level.parameters" value=“true"/> Log4jdbc-remix: - Resultsets als Tables - Sonatype Maven Repository

134 Minimizing Query Count
Typischer Weise entstehen die ersten Persistenz bezogenen Performance Probleme aufgund der Anzahl Queries auf die DB Analyse Optimierung des Globalen Fetch Plans Optimierung des Fetch Plans auf Use-Case Ebene Caching in der Applikation (z.B. DAO, Manager) 2nd Level Cache / Query Cache “Reporting View” mit Constructor Expressions

135 Teure Queries Analyse, auch auf DB Ebene (Execution-Plan)
Meist hilft das setzten eines Index in der DB Optimierung der Fetch-Strategie Optimierung von Queries Verwendung von Query Hints Verwendung von SQL-Queries

136 Named Queries Named Queries können vom JPA-Provider beim Deploy oder Startup-Zeitpunkt geparst und kompiliert werden. Das resultierende prepared Statement kann dann wiederverwendet werden.

137 Instanzierung und Marshalling
Result Sets Objekte / Objekt-Graphen Statements In Typischen Anwendungen ist dies kaum ein Performance Problem. Objektinstanzierung ist um Grössenordnungen schneller als DB-Query Bei sehr grossen Datenmengen können Probleme entstehen aufgrund der Session Grösse (Identity-Map)

138 Constructor Expressions
Erlaubt “Views” auf Daten, welche nicht den Entity-Strukturen entspricht.

139 Session Grösse Eine grosse Session kann zu Performance Problemen führen Out of Memory Garbage Collector Identity Map Typischerweise ein Problem in Batch Szenarien Frühe Erkennung während des Designs sinnvoll Anwendung einer anderen Persistenz Strategie Achtung: Session-Management sollte von Transktions-Steuerung getrennt behandelt werden!

140 Session Grösse Lösungsansätze Regelmässiges flush() und clear()
Bulk-Statements in JPQL Direktes SQL über JDBC Proprietäre Mechanismen des JPA-Providers, z.B. Hibernate Stateless Session

141 Entity Caching Explizites Caching in der Applikation
z.B. DAO, Business-Komponente, Application-Infrastruktur Lifetime kann explizit kontrolliert werden (z.B. Request, Conversation, Session, Application) Verwendung eines transparenten Caching Mechanismus (2nd Level Cache) Lernkurve! Oft beinahe “Black Magic”. Grösse, Invalidation, Transaction-Isolation ...

142 Query Caching Etliche JPA Providers ermöglichen das Caching von Query-Results. Ist nur in sehr seltenen Fällen sinnvoll Queries sind meist parametrisiert ... Invalidation und Read/Write Verhältnis

143 Objektrelationales Mapping mit JPA 2.0
Testing Objektrelationales Mapping mit JPA 2.0

144 Testen mit Einbezug der DB
Schwierigkeiten: Die DB repräsentiert den Zustand unseres Systems Oft enthält die DB den Input und den Output unseres Tests Um gezielt und deterministisch zu Testen müssen wir den DB Zustand kennen und beeinflussen Zusätzlich: Fast immer enthält die DB auch Logik (Constraints sind auch Logik)

145 Unit- vs. Integration Tests
Mocking der DB soweit möglich: Einbindung der DB DB in einen definierten Zustand bringen Tests durchführen Veränderungen verifizieren (Aufräumen)

146 Mocking DAOs sind ein guter Ansatzpunkt Mocking der JPA-Infrastrukur
Zentraler Zugriffspunkt, klares Interface Formulierung der Erwartungen an den Data-Access Layer mit Business Semantik Erwartungen können gegen die DB gestestet werden Mocking der JPA-Infrastrukur Möglich, aber aufwändig (EntityManager, Query) Viel technische Komplexität, kein Business Value

147 In-Memory Datenbank HSQLDB, H2 und Derby sind “reife” Datenbanken mit einem In-Memory Mode DB wird für jeden Test neu gestartet und initialisiert Einfache Integration, lokale Datenbank Keine Seiteneffekte Schema immer aktuell Die meisten Persistenz bezogenen Probleme können erkannt werden

148 Mehrstufige Testing Strategie
Unit-Tests mit Mocking Testen von Business Komponenten in Isolation Korrektes Verhalten bezüglich erwartetem Business Funktionale Tests mit In-Memory Datenbank Korrektes Verhalten mit Einbezug der Persistenz-API Sehr einfache Integration, schnell, lokal Setup/Teardown pro Test Integrations Tests gegen Produktions-Datenbank Produktives Verhalten Wenn möglich mit produktiven Datenbeständen Komplizierteres Setup/Teardown, langsam

149 Testdaten Management Wichtiges Thema das früh im Projekt angegangen werden sollte Wer liefert valide Testdaten Wie und wann werden Testdaten in die verschiedenen DBs eingespielt Schema-Migrationen Patterns für Unit-Tests: Object Mother, Test Data Builder

150 Objektrelationales Mapping mit JPA 2.0
Architecture Objektrelationales Mapping mit JPA 2.0 150

151 Java EE 6 Integration

152 JSF 2.0 und Bean Validation
<h:inputText id="name" value="#{employeeBean.employee.name}"> <f:validateBean for="name"/> </h:inputText> <h:message for="name" />

153 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»!

154 EJB 3.x Dependency Injection
@PersistenceUnit Liefert eine Referenz zur EntityMangerFactory @PersistenceContext Liefert eine Referenz zum EntityManger @PersistenceUnit private EntityManagerFactory emf; @PersistenceContext private EntityManager em;

155 JPA 2.0 und JSR 303 @NotNull statt @Column(nullable=false)
@Size.max @Digits @Min bei numerischen Columns @Future bei Datumstypen @Size für Collections und Arrays

156 Stateless Session Beans
public class EmployeeService { @PersistenceContext( type=PersistenceContextType.TRANSACTION) private EntityManager em; public Employee findById(Integer id) { Employee e = em.find(Employee.class, id); return e; }

157 Stateful Session Beans
public class EmployeeService { @PersistenceContext( type=PersistenceContextType.EXTENDED) private EntityManager em; public Employee findById(Integer id) { Employee e = em.find(Employee.class, id); return e; }

158 Persistence Unit <persistence>
<persistence-unit name="jpa.sbb" transaction-type="JTA"> <jta-data-source>jdbc/emp</jta-data-source> </persistence-unit> </persistence>

159 JPA 2.0 und JSR 303 @NotNull statt @Column(nullable=false)
@Size.max @Digits @Min bei numerischen Columns @Future bei Datumstypen @Size für Collections und Arrays

160 Patterns

161 Data Access Object (DAO)
Problem You want to encapsulate data access and manipulation in a separate layer Solution Use 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

162 DAO Klassendiagramm

163 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); }

164 Transfer Object (TO) Problem You want to transfer multiple data elements over a tier Solution Use 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

165 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); }

166 Schichtung und Verteilung
3rd Party Ausschliesslich TOs Applikation XY Client oder Webserver Applikationsserver DB Server Präsentations-schicht Service Fassade Business Datenzugriff Datenhaltung TOs und Entities

167 Client/Server Entities Ausschliesslich TOs

168 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

169 Open Session in View Lazy O1 O1 HTTP Session Transaktion 1
Java Persistence API

170 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

171 Preload Pattern nach Jürgen Kohl (2)
public class Preload { private Class modelClass; private String property; public Preload(Class modelClass, String property) { this.modelClass = modelClass; this.property = property; } } Java Persistence API

172 Preload Pattern nach Jürgen Kohl (3)
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); } } Java Persistence API

173 Preload Pattern nach Jürgen Kohl (4)
// 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); } } Java Persistence API

174 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: @Entity public class Master { @OneToOne(fetch=FetchType.LAZY) private Detail detail; } JXPathContext.newContext(master).getValue("/detail"); Java Persistence API

175 Batch Problematik Lösung Vorsicht
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())

176 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

177 Objektrelationales Mapping mit JPA 2.0
Advanced Topics Objektrelationales Mapping mit JPA 2.0 177

178 SQL Queries EntityManager.createNativeQuery(String sql)
EntityManager.createNativeQuery(String sql, Class resultClass) Kann auch als NamedQuery definiert werden: Ausführen des NamedQueries und Parameterübergabe: @NamedNativeQuery( name = "employeeReporting", query = "SELECT * FROM emp WHERE id = ?", resultClass = Employee.class) Query q = em.createNamedQuery("employeeReporting"); q.setParameter(1, employeeId); q.getResultList()

179 Result Set Mapping (1) Definition Verwendung
@SqlResultSetMapping( name = "employeeResult", entities = Employee.class) Query q = em.createNativeQuery( "SELECT * FROM EMPLOYEE", "employeeResult");

180 Result Set Mapping (2) Mapping Foreign Keys
Wenn die Foreign Keys im Resultat der Abfrage enthalten sind, werden die einfachen Beziehungen ebenfalls mitgeführt Multiple Result Mapping SELECT e.*, a.* FROM emp e, address a WHERE e.adress_id = a.id @SqlResultSetMapping( name = "EmployeeWithAddress", entities = = Employee.class), @EntityResult(entityClass = Address.class)}

181 Result Set Mapping (3) Mapping Inheritance SELECT id, name, start_date, daily_rate, term, vacation, hourly_rate, salary, pension, type FROM employee_stage @SqlResultSetMapping( name="EmployeeStageMapping", entities= @EntityResult( entityClass=Employee.class, discriminatorColumn="TYPE", fields={ @FieldResult(name="startDate",column="START_DATE"), @FieldResult(name="dailyRate",column="DAILY_RATE"), @FieldResult(name="hourlyRate",column="HOURLY_RATE") } ) )

182 Lifecycle Callbacks Callbacks sind eine gängige Methode, um Einfluss auf den Lade- oder Speichervorgang von Objekten zu nehmen. Mögliche Events: PrePersist, PostPersist, PreRemove, PostRemove, PreUpdate, PostUpdate, PostLoad @Entity public class ImageMessage extends Message { protected void compress(){ ... } protected void uncompress() { ... }

183 Entity Listeners Wenn Sie dieselbe Funktionalität bei Callbacks von verschiedenen Entities verweden wollen, können Sie einen Entity Listener erstellen: Entity Listener verwenden: public class BaseEntityDebugListener { @PrePersist public void debugPrePersist(BaseEntity e) { log.debug("PrePresist: " + e); } } public class Employee extends BaseEntity

184 Vererbung und Lifecycle Events
Prüfen ob Default Listeners existieren Höchste Ebene der Vererbung Annotations prüfen Wiederhole Schritt 2 für die nächst tiefere Ebene Höchste Ebene der Vererbung auf Methoden mit zum Event passender Annotation prüfen Wiederhole Schritt 4 für die nächst tiefere Ebene

185 Sperrzeitpunkt Daten werden in der Datenbank im Rahmen von SQL-Befehlen gesperrt. Da Änderungen vorerst lokal durchgeführt und erst zum Commit-Zeitpunkt in die DB propagiert werden, findet das Sperren erst beim Commit statt. Allfällige Wartesituationen, Deadlocks, Integritätsverletzungen treten effektiv erst zum Commit-Zeitpunkt auf.

186 Optimistic Locking / Versionierung
Die Versionierung im Rahmen von JPA ist als optimistsches Locking zu verstehen. Mit der Versionierung kann ein Concurrency Control über Transaktionen hinweg realisiert werden. Mit können Lost Updates detektiert und vermieden werden: Das Feld kann int, Integer, short, Short, long, Long oder java.sql.Timestamp sein @Entity public class Employee { @Version protected long version; create table emp ( version bigint not null, ...

187 Weitere Locking Strategien
Soll REPEATABLE_READ zur Anwendung kommen, kann Read Locking verwendet werden: em.lock(LockModeType.READ) Um eine Entität zum Schreiben zu sperren, wird Write Locking verwendet em.lock(LockModeType.WRITE) Die Verwendung von anderen Isolationlevels, muss über die JDBC Connection erfolgen

188 Enhanced Locking (JPA 2.0)
JPA 2.0 führt neue Lockingstrategien ein: OPTIMISTIC ( = READ ) OPTIMISTIC_FORCE_INCREMENT ( = WRITE ) PESSIMISTIC_READ  Repeatable Read PESSIMISTIC_WRITE  Serialized PESSIMISTIC_FORCE_INCREMENT -> With version field Kombination Opitmistic/Pessimistic möglich!

189 Enhanced Locking (JPA 2.0)
Example in EntityManager.refresh() public void applyCharges() { Account acct = em.find(Account.class, acctId); // calculate charges, etc. int charge = … ; if (charge > 0) { em.refresh(acct, PESSIMISTIC_WRITE); double balance = acct.getBalance(); acct.setBalance(balance - charge); }

190 Schema Generation Die Erzeugung von DDL kann, aber muss nicht durch eine Implementation der JPA Spezifikation angeboten werden. In der JPA Spezifikation sind verschiedene Angaben (Annotations und Attribute davon) vorgesehen, welche die Erzeugung von DDL-Befehlen ermöglichen public class Message { @Column (unique = false, nullable=false, columnDefinition="varchar", lenghth=64 ) public BigDecimal sender;

191 XML Mapping

192 The Metadata Puzzle Reihenfolge bei der Verarbeitung der Metadaten:
Annotations verarbeiten Klassen aus XML Mapping hinzufügen Attribute aus XML Mapping hinzufügen Defaultwerte setzen

193 Das Mapping File Rootelement is entity-mappings:
<?xml version="1.0" encoding="UTF-8"?> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://java.sun.com/xml/ns/persistence/orm version="2.0">

194 Defaults im Mapping File
Annotations ausschalten <xml-mapping-metadata-complete /> Persistence Unit Defaults schema catalog access cascade-persist entity-listeners Diese können auch ausgeschaltet werden exclude-default-listeners @ExcludeDefaultListeners

195 Defaults im Mapping File
Mapping File Defaults package schema catalog access

196 Queries und Generators
sequence-generator table-generator named-query named-native-query sql-result-set-mapping

197 Entity Mapping mit XML <entity class="examples.model.Employee"> <attributes> <id name="id"> <generated-value strategy="SEQUENCE" /> </id> <many-to-one name="department" /> <many-to-one name="manager" /> <one-to-many name="phones" mapped-by="employee" /> <one-to-many name="directs" mapped-by="manager" /> <one-to-one name="address" /> <many-to-many name="projects" mapped-by="employees" /> </attributes> </entity>

198 Packaging and Deployment

199 Configuring Persistence Units
Persistence Unit Name <persistence-unit name="EmployeeService" /> Transaction Type RESOURCE_LOCAL oder JTA Persistence Provider <provider>org.hibernate.ejb.HibernatePersistence</provider> Data Source nur bei JTA Mapping Files <mapping-file>META-INF/queries.xml</mapping-file>

200 Managed Classes Lokale Klassen Alle annotierten Klassen im Deployment Unit in welcher das persistence.xml gepackt ist Klassen in Mapping Files Mit XML gemappte Klassen Explicitly Listed Classes Im persistence.xml eingetragene Klassen Zusätzliche JARs mit Managed Classes Im persistence.xml unter jar-file eingetragene JARs

201 Properites zur Laufzeit übergeben
Die properties im persistence.xml können ebenfalls zur Laufzeit beim Erstellen der EntityManagerFactory übergeben werden: Map props = new HashMap(); props.put("toplink.jdbc.user", "emp"); props.put("toplink.jdbc.password", "emp"); EntityManagerFactory emf = Persistence .createEntityManagerFactory("EmployeeService", props);

202 Tools

203 IDE Integration Eclipse NetBeans IntelliJ IDEA Projekt DALI
Oracle Enterprise Pack for Eclipse JBoss Tools NetBeans IntelliJ IDEA

204 Zusammenfassung / Ausblick
Objektrelationales Mapping mit JPA 2.0

205 JPA 1.0 & 2.0 JPA 1 is inzwischen mehr als 4 Jahre alt
JPA 2 ist eine eigenständge Spezifikation, welche auf JPA 1 aufbaut und etliche zusätzliche Features bietet JSR 317, final release: EclipseLink ist die Referenzimplementation JPA 2 Provider: EclipseLink 2.0, Hibernate 3.5, OpenJPA 2.0, DataNucleus 2.1

206 Zusammenfassung JPA 2.0 Features
Mixing field- and property-access in one Entity Unidirectional one-to-many without join-table Ordered lists with persistent order Collections of basic and embeddable types Orphan deletion for collections Embeddables can be nested and have relationships Improved support for maps Pessimistic locking Caching standardization Improvements in JPQL (collection-valued expressions, polymorphism …) Criteria API Metadata Model API More standardized properties for configuration Integration with Bean Validation (JSR-303)

207 Beyond JPA 2.0 FlushMode.MANUAL Standardized Audit / Versioning API
Configurable mapping for Enum values to DB values More sensible column-naming for Embeddables


Herunterladen ppt "Objektrelationales Mapping mit JPA 2.0"

Ähnliche Präsentationen


Google-Anzeigen