Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

25
Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli

Transcript of Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Page 1: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Objektrelationales Mapping mit JPA

Querying

Jonas BandiSimon Martinelli

Page 2: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Entitäten 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

Page 3: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Entity über die ID ladenDer 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.

Page 4: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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

Page 5: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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();

Page 6: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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.

Page 7: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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();

Page 8: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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();

Page 9: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Parameter Binding

Queries können parametrisert werden. Es gibt zwei Arten der Parametrisierung:

• Named Paramters

• Positional Parameters

SELECT e FROM Employee eWHERE e.department = :dept AND e.salary > :base

Query q = ...q.setParameter("dept", "Taxes");q.setParameter("base", "3500");

SELECT e FROM Employee eWHERE e.department = ?1 AND e.salary > ?2

Query q = ...q.setParameter(1, "Taxes");q.setParameter(2, "3500");

Page 10: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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

Page 11: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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,

Page 12: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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

Page 13: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Pagination

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 ist für den entsprechenden SQL-Dialekt optimiert.

– Achtung: Meist wird das SQL-Rowset limitiert, und nicht die resultierenden Entities!

Mit JPA ist Pagination sehr einfach:

Page 14: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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

Page 15: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Fetching & Lazy Loading

@OneToMany(mappedBy = "employee", fetch = FetchType.EAGER)private Set<Phone> phones = new HashSet<Phone>();

• 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

SELECT d FROM Department d LEFT JOIN FETCH d.employees

Page 16: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Joins & Fetching

Es gibt unterschiedliche Joins in JPQL:

SELECT d FROM Department d LEFT JOIN FETCH d.employees

• Explizite Joins für Selektion und Projektion

• Implizite Joins aus Pfadausdrücken

• Fetch Joins für Eager Loading

SELECT e FROM Employee e where e.address.city = 'Bern'

SELECT employee FROM Employee employee JOIN employee.projects project WHERE project.name = 'Arcos'

SELECT e.address FROM Employee e where e.name = 'John'

SELECT project FROM Employee employee JOIN employee.projects project WHERE employee.name = 'John'

Page 17: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Polymorphe Queries

Query q = em.createQuery("select p FROM Project p");

List<Project> projects = q.getResultList();

JPQL unterstützt Polymorphie:

• 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)

Selektion aufgrund einer Subklasse:SELECT employee FROM Employee employee JOIN employee.projects project, DesignProject dprojectWHERE project = dproject AND dproject.innovationLevel > 2

Page 18: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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!

Page 19: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Constructor Expressions

Mit Constructor Expressions existiert eine einfache Möglichkeit um Resultate auf Klassen zu mappen:

public class EmployeeTO { public String employeeName; public String deptName; public EmployeeTO(String employeeName, String deptName) {...}}

List result = em.createQuery( "SELECT NEW jpa.util.EmployeeTO(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.

Page 20: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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.

Page 21: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Vorteile und Nachteile von JPQL

• Vorteile

– 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

Page 22: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

• Stored Procedures werden in JPA nicht unterstützt. Die meisten JPA-Implementationen bieten jedoch proprietäre Mechanismen zum Einbinden von Stored Procedures.

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

Page 23: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

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

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();

@SqlResultSetMapping(name = "EmployeeWithAddress",entities = {@EntityResult(entityClass = Employee.class), @EntityResult(entityClass = Address.class)}

Page 24: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Criteria API in JPA 2

Mit der Criteria API wird in JPA 2 eine Objekt-Orientierte Schnittstelle zum programmatischen Erstellen von Queries standardisiert.

– Die meisten JPA-Implementationen bieten bereits eine proprietäre Schnittstelle dieser Art an

• Vorteile:

– Dynamisches Erstellen von Queries (Komposition)

– Keine String-Manipulation notwendig

– OO-Konstrukte zum Erstellen komplexer Queries

– Gewisse Typsicherheit

Page 25: Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli.

Criteria API in JPA 2Beispiel mit Strings für Referenzen:

Beispiel mit typsicheren, statischem Metamodel:

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"));

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"));