25
Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli

Objektrelationales Mapping mit JPA Querying Jonas Bandi Simon Martinelli

Embed Size (px)

Citation preview

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