2. Dirk Weil
GEDOPLAN GmbH, Bielefeld
Java EE seit 1998
Konzeption und
Realisierung
Seminare
Vorträge
Veröffentlichungen
Speeding up Java Persistence 2
3. Id-Generierung
Entity-Klassen müssen Id haben
PK in der DB
Feld oder
Property
mit @Id
Empfehlenswert: Technische Id
Problem: Erzeugung eindeutiger Werte
@Entity
public class SomeEntity
{
@Id
private int id;
…
Speeding up Java Persistence 3
5. Id-Generierung
Alternative: BYO-ID (selbst machen)
Id auch in transitiven Objekten gesetzt
Insert ohne Zusatzaufwand
Achtung: i. A. nicht trivial
Z. B.: UUID
this.id = UUID.randomUUID().toString();
// oder: new com.eaio.uuid.UUID().toString()
Speeding up Java Persistence 5
9. Relationship Loading
Relationen werden durch Attribute mit @…To… repräsentiert
@Entity
public class Book
{
@ManyToOne
public Publisher publisher;
@Entity
public class Publisher
{
@OneToMany(mappedBy="publisher")
public List<Book> books;
Speeding up Java Persistence 9
11. Relationship Loading
Bsp.: Auftragsposition bearbeiten
n:1-Relation zu Artikel
Kunde Auftrag
Auftrags
Position
ArtikelLand
1 *
1
*
*
1
1*
@Entity
public class AuftragsPosition
{
@ManyToOne
private Artikel artikel;
Speeding up Java Persistence 11
12. Relationship Loading
Annahme:
Verwendet nur
AuftragsPosition
AuftragsPosition aufPos
= em.find(AuftragsPosition.class, id);
…
select …
from AuftragsPosition
where …
@ManyToOne
private Artikel artikel;
@ManyToOne(fetch=FetchType.LAZY)
private Artikel artikel;
select … from AuftragsPosition where …
select … from Artikel where …
Speeding up Java Persistence 12
13. Relationship Loading
Annahme:
Verwendet auch
Artikel
AuftragsPosition aufPos
= em.find(AuftragsPosition.class, id);
…
Artikel artikel = aufPos.getArtikel();
…
@ManyToOne
private Artikel artikel;
@ManyToOne(fetch=FetchType.LAZY)
private Artikel artikel;
select … from AuftragsPosition where …
select … from Artikel where …
select … from AuftragsPosition where …
…
select … from Artikel where …
Speeding up Java Persistence 13
14. Relationship Loading
Messergebnis (1000 Items, Hibernate, MySQL)
14Speeding up Java Persistence
EAGER LAZY
AuftragsPosition ohne Artikel 2.967 ms 2.505 ms - 15 %
AuftragsPosition mit Artikel 2.959 ms 4.305 ms + 45 %
Kunde ohne Auftrag 30.295 ms 4.848 ms - 84 %
=Default
Kunde Auftrag
Auftrags
Position
ArtikelLand
1 *
1
*
*
1
1*
15. Relationship Loading
Fazit:
Zugriffsverhalten genau analysieren
Default ist schon recht gut
Besser: Immer LAZY verwenden
und bei Bedarf Fetch Joins oder Entity Graphs nutzen
Speeding up Java Persistence 15
16. Relationship Loading
Fetch Joins
mit JPQL
Achtung: Kartesisches Produkt!
select ap from Auftragsposition ap
left join fetch ap.artikel
...
Speeding up Java Persistence 16
17. Relationship Loading
Fetch Joins
mit Criteria Query
CriteriaQuery<Auftrag> cQuery
= builder.createQuery(Auftrag.class);
Root<Auftrag> a = cQuery.from(Auftrag.class);
a.fetch(Auftrag_.auftragsPositionen, JoinType.LEFT)
.fetch(AuftragsPosition_.artikel, JoinType.LEFT);
…
Speeding up Java Persistence 17
18. Relationship Loading
Entity Graphs
@Entity
@NamedEntityGraph(
name = "Kunde.auftraege",
attributeNodes = @NamedAttributeNode(value = "auftraege")))
public class Kunde
{
@OneToMany(mappedBy="kunde")
private Set<Auftrag> auftraege;
TypedQuery<Kunde> query = entityManager.createQuery(…);
EntityGraph<?> entityGraph
= entityManager.getEntityGraph("Kunde.auftraege");
query.setHint("javax.persistence.loadgraph", entityGraph);
Speeding up Java Persistence 18
19. Fetch Tuning
ToMany-Fetching: "N+1"-Problem
z.B. Lesen einiger User inkl. Groups + Permissions
SELECT ... FROM USER
SELECT ... FROM GROUP WHERE USER_ID=?
SELECT ... FROM PERMISSION WHERE GROUP_ID=?
SELECT ... FROM PERMISSION WHERE GROUP_ID=?
SELECT ... FROM GROUP WHERE USER_ID=?
SELECT ... FROM GROUP WHERE USER_ID=?
Speeding up Java Persistence 19
21. Fetch Tuning
Lösungsansatz 2: Batch Fetching
@ManyToMany(fetch = FetchType.LAZY, …)
@BatchFetch(value = BatchFetchType.IN, size = 10)
private Set<Group> groups;
Annotation Relationsauflösung
EclipseLink @BatchFetch IN, EXISTS, JOIN
Hibernate @BatchSize IN, EXISTS
SELECT ... FROM USER
SELECT ... FROM GROUP WHERE USER_ID IN (?,?,…)
SELECT ... FROM PERMISSION WHERE GROUP_ID IN (?,?,…)
SELECT ... FROM PERMISSION WHERE GROUP_ID IN (?,?,…)
SELECT ... FROM GROUP WHERE USER_ID IN (?,?,…)
N+1
(N/B)+1
Speeding up Java Persistence 21
22. Basic Attribute Loading
Fetch-Strategie auch für einfache Werte wählbar
Lazy Loading ggf. sinnvoll bei
selten genutzten Werten
umfangreichen Daten
@Basic(fetch = FetchType.LAZY)
private String longAdditionalInfo;
Speeding up Java Persistence 22
23. Basic Attribute Loading
Messergebnis
Lesen von Kunden
10 'ungenutzte' Strings à 150 chars
1000 Interationen, Hibernate, MySQL
EAGER LAZY
6.782 ms 6.440 ms -5 %
= Default-Einstellung
Speeding up Java Persistence 23
24. Tupel-Selects
Selektion einzelner Attribute
als Tupel
mit Constructor Expression
Messergebnis (ms für 10000 Items)
select k.id, k.name from Kunde k
select new IdAndName(k.id, k.name) from Kunde k
Entity komplett Object[] Ctor Expression
11.211 ms 3.890 ms 4.010 ms
Speeding up Java Persistence 24
25. Query-Parameter
JPQL Injection
Prepared Statement Reuse
em.createQuery("select k from Kunde k "
+ "where k.name='" + someString + "'")
em.createQuery("select k from Kunde k "
+ "where k.name=:name")
.setParameter("name", someString)
Speeding up Java Persistence 25
27. First Level Cache
Standard
Je EntityManager
Enthält in Sitzung geladene Objekte
Achtung: Speicherbedarf!
sinnvolle EM-Lebensdauer nutzen
flush/clear ist i. A. Antipattern!
ausser im Batch – dort häufig sinnvoll
EntityManager
1st
Level
Cache
Speeding up Java Persistence 27
28. First Level Cache
Arbeitet
sitzungs-
bezogen
// Kunden mit bestimmter Id laden
EntityManager em1 = emf.createEntityManager();
Kunde k1 = em1.find(Kunde.class, id);
// Gleichen Kunden in 2. Session verändern
EntityManager em2 = emf.createEntityManager();
em2.getTransaction().begin();
Kunde k2 = em2.find(Kunde.class, id);
k2.setName("…");
em2.getTransaction().commit();
// Gleichen Kunden in 1. Session erneut laden
Kunde k3 = em1.find(Kunde.class, id);
// ist unverändert!
Speeding up Java Persistence 28
29. First Level Cache
HashMap-Semantik
benötigt Key
wird für Queries nicht benutzt
// Kunden mit bestimmter Id laden
EntityManager em = emf.createEntityManager();
Kunde k1 = em.find(Kunde.class, id);
// Query nach gleichem Kunden geht erneut zur DB!
Kunde k2 = em.createQuery("select k from Kunde k " +
"where k.id=:id", Kunde.class)
.setParameter("id", id)
.getSingleResult();
Speeding up Java Persistence 29
30. Query Cache
Provider-spezifisch
Speichert Result Set IDs zu Queries
TypedQuery<Kunde> query
= em.createQuery("select k from Kunde k where k.name=:name",
Kunde.class);
query.setParameter("name", "OPQ GbR");
… // Query Cache einschalten
Kunde kunde = query.getSingleResult();
["select k from Kunde k where k.name=:name", "OPQ GbR"] [id1]
Speeding up Java Persistence 30
31. Query Cache
Trotz mehrfacher Query
nur ein DB-Zugriff
while (…)
{
TypedQuery<Kunde> query
= em.createQuery("select k from Kunde k where k.name=:name",
Kunde.class);
query.setParameter("name", "OPQ GbR");
query.setHint(…) // Query Cache einschalten (providerabh.!)
Kunde kunde = query.getSingleResult();
…
}
Speeding up Java Persistence 31
35. Second Level Cache
Wirkt applikationsweit
Semantik ähnlich HashMap
Ladereihenfolge:
1st Level Cache (EntityManager)
2nd Level Cache, falls enabled
DB
Speeding up Java Persistence 35
36. Second Level Cache
Vorteil bei häufig genutzten Daten
Konstanten
selten veränderte Daten
nur von dieser Anwendung veränderte Daten
Speeding up Java Persistence 36
37. Second Level Cache
Bsp.: Stammdaten-Entity Land
wird n:1 von Kunde
referenziert
nur wenige Land-Werte
Länder ändern sich nahezu nie
Länder können dauerhaft im Cache verbleiben
Kunde
Land
1
*
Speeding up Java Persistence 37
38. Second Level Cache
Konfiguration lt. Spec
<persistence-unit name="…">
<provider>…</provider>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
…
Cache aktiv für … Default bei …
ALL alle Entities
NONE keine Klasse Hibernate
ENABLE_SELECTIVE nur @Cacheable(true)
DISABLE_SELECTIVE alle außer @Cacheable(false) EclipseLink
@Entity
@Cacheable(true)
public class Land
{
…
Speeding up Java Persistence 38
40. Second Level Cache
L2C für Anwendungscode transparent
find liefert Kopie des Cache-Eintrags
Für komplett konstante Werte kann ein weiterer Cache in der
Anwendung sinnvoll sein
Speeding up Java Persistence 40
41. Paginierung
Queries mit großer Ergebnismenge
'häppchenweise' verarbeiten
TypedQuery<Artikel> query
= em.createQuery("select a from Artikel a", Artikel.class);
query.setFirstResult(50);
query.setMaxResults(10);
List<Artikel> result = query.getResultList();
select …
from Artikel
where … and rownum>=50 and rownum<60
Speeding up Java Persistence 41
42. Paginierung
Eingeschränkt oder effektlos bei 1:n/m:n-Relationen mit:
Eager Loading
Fetch Joins
Join erzeugt kartesisches Produkt
Providerabhängige Lösung:
Ausführung im Memory
Ausführung mehrerer SQL-Befehle
Speeding up Java Persistence 42
43. Inheritance
Mehrere Abbildungen denkbar:
Alles in einer Tabelle
Eine Tabelle pro Klasse
Eine Tabelle pro konkreter Klasse
Strategie-Auswahl mit @Inheritance
<abstract>
Vehicle
Car Ship
Speeding up Java Persistence 43
47. Inheritance
Laufzeitvergleich für Queries
auf Basisklasse
auf abgeleitete Klasse
(1000 Iterationen, Ergebnis ca. 100 Einträge, Hibernate, MySQL)
SINGLE_
TABLE
TABLE_
PER_CLASS
JOINED
Basisklasse 2.705 ms 29.359 ms 3.434 ms
Subklasse 2.505 ms 1.435 ms 3.377 ms
Speeding up Java Persistence 47