optimierung von jpa-anwendungen
DESCRIPTION
War Persistenz in Java EE früher schwergewichtig und unflexibel, so steht nun der leichtgewichtige Standard JPA mit Providern wie EclipseLink und Hibernate zur Verfügung. Die Einfachheit ist bestechend, verleitet aber auch zu unbedachtem Einsatz mit teilweise enttäuschender Performanz. Der Vortrag zeigt wie JPA-Anwendungen auf den nötigen Durchsatz hin optimiert werden können.TRANSCRIPT
![Page 1: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/1.jpg)
Optimierung von JPA-Anwendungen
Java User Group Berlin Brandenburg, 31.01.2013
Dirk Weil, GEDOPLAN GmbH
![Page 2: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/2.jpg)
Dirk Weil
GEDOPLAN GmbH, Bielefeld
Java EE seit 1998
Konzeption undRealisierung
Vorträge
Seminare
Veröffentlichungen
![Page 3: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/3.jpg)
Optimierung von JPA-Anwendungen
3
Laufzeit
Memory
Providerunabhängig
EclipseLink
Hibernate
…
![Page 4: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/4.jpg)
Id-Generierung
Entity-Klassen müssen Id haben
PK in der DB
Feld oderPropertymit @Id
Empfehlenswert: Technische Id
Problem: Erzeugung eindeutiger Werte
4
@Entitypublic class SomeEntity{
@Idprivate int id;…
![Page 5: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/5.jpg)
Id-Generierung
JPA-Feature: @GeneratedValue
Nutzt DB-Sequenzen,Identity Columns oderSequenz-Tabellen
Probleme:
Id erst nach persist gesetzt� equals ?, hashCode ?
Id-Übernahme kostet Zeit
5
@Id@GeneratedValueprivate int id;
![Page 6: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/6.jpg)
Id-Generierung
Alternative: BYO-ID (selbst machen)
Id auch in transitiven Objekten gesetzt
Insert ohne Zusatzaufwand
Achtung: i. A. nicht trivial
Z. B.: UUID
6
@Idprivate String id
= new com.eaio.uuid.UUID().toString();
![Page 7: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/7.jpg)
Id-Generierung
@GeneratedValue signifikant langsamer(OOTB)
7
Derby
EclipseLink
MySQL
EclipseLink
Oracle
EclipseLink
Derby
Hibernate
MySQL
Hibernate
Oracle
Hibernate
BYO-ID 19.864 11.659 22.478 19.240 9.684 7.126
AUTO 21.034 13.537 23.663 74.804 12.214 70.814
0
10.000
20.000
30.000
40.000
50.000
60.000
70.000
80.000
Mil
lise
ku
nd
en
Insert 50.000 einfache Entries
in 1.000er Batches
![Page 8: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/8.jpg)
Id-Generierung
Tuning: Höhere Allocation Size
Leider nicht verfügbar bei IDENTITY
8
@Id@GeneratedValue(strategy = GenerationType. SEQUENCE,
generator = "ArtikelIdGenerator")@SequenceGenerator (name = "ArtikelIdGenerator",
allocationSize = 1000 )private int id;@Id
@GeneratedValue(strategy = GenerationType. TABLE,generator = "ArtikelIdGenerator")
@TableGenerator (name = "ArtikelIdGenerator",allocationSize = 1000 )
private int id;
![Page 9: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/9.jpg)
Id-Generierung
9
0
10.000
20.000
30.000
40.000
50.000
60.000
70.000
80.000
1 10 100 1000
Laufzeit vs. Allocation Size
![Page 10: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/10.jpg)
Relationship Loading
Relationen werden durch Felder mit @OneToOne, …, @ManyToManyrepräsentiert
10
@Entitypublic class Book{
@ManyToOnepublic Publisher publisher;
@Entitypublic class Publisher{
@OneToMany(mappedBy="publisher")public List<Book> books;
![Page 11: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/11.jpg)
Relationship Loading
Relationen-Parameter: fetch
Referenzierte Entities direkt laden?
EAGER: Direkt
LAZY: Später bei Bedarf
11
@ManyToOne(fetch = FetchType.LAZY )private Artikel artikel;
![Page 12: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/12.jpg)
Relationship Loading
Bsp.: Auftragsposition bearbeiten
Ist Owner der n:1-Relation zu Artikel
12
Kunde Auftrag
AuftragsPosition
ArtikelLand
1 *
1*
*1
1*
@Entitypublic class AuftragsPosition{
@ManyToOneprivate Artikel artikel;
![Page 13: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/13.jpg)
Relationship Loading
Annahme:Verwendet nurAuftragsPosition
13
AuftragsPosition aufPos= em.find(AuftragsPosition.class, id);…
select …from AuftragsPositionwhere …
select …from AuftragsPositionleft outer join Artikelwhere …
@ManyToOneprivate Artikel artikel;
@ManyToOne(fetch=FetchType.LAZY )private Artikel artikel;
![Page 14: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/14.jpg)
Relationship Loading
Annahme:Verwendet auchArtikel
14
AuftragsPosition aufPos= em.find(AuftragsPosition.class, id);Artikel artikel = aufPos.getArtikel();…
select … from AuftragsPosition where …
select … from Artikel where …
select …from AuftragsPositionleft outer join Artikelwhere …
@ManyToOneprivate Artikel artikel;
@ManyToOne(fetch=FetchType.LAZY )private Artikel artikel;
![Page 15: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/15.jpg)
Relationship Loading
Bsp.: Kunde bearbeiten
Ist Owner der 1:n-Relation zu Auftrag
15
Kunde Auftrag
AuftragsPosition
ArtikelLand
1 *
1*
*1
1*
@Entitypublic class Kunde{
@OneToMany(mappedBy="kunde")private Set<Auftrag> auftraege;
![Page 16: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/16.jpg)
Relationship Loading
Annahme:Verwendetnur Kunde
16
Kunde kunde= em.find(Kunde.class, id);…
select …from Kundewhere …
select …from Kundeleft outer join Auftragleft outer join AuftragsPosition where …
@ManyToOne(fetch=FetchType.EAGER )private Set<Auftrag> auftraege;
@ManyToOneprivate Set<Auftrag> auftraege;
![Page 17: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/17.jpg)
Relationship Loading
Messergebnis(1000 Interationen, Hibernate, MySQL)
17
EAGER LAZY
Nur AuftragsPosition 2.967 ms 2.505 ms - 15 %
Auch Artikel 2.959 ms 4.305 ms + 45 %
Nur Kunde 30.295 ms 4.848 ms - 84 %
= Default-Einstellung
![Page 18: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/18.jpg)
Relationship Loading
Fazit:
Zugriffsverhalten genau analysieren
Default ist schon recht gut
Besser: Immer LAZY verwendenund bei Bedarf Fetch Joins nutzen
18
![Page 19: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/19.jpg)
Relationship Loading
Fetch Joins mit JPQL
leider nur einstufig erlaubt
19
select ap from Auftragsposition apleft fetch join ap.artikel...
![Page 20: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/20.jpg)
Relationship Loading
Fetch Joins mit Criteria Query
20
CriteriaQuery<Auftrag> cQuery= builder.createQuery(Auftrag.class);
Root<Auftrag> a= cQuery.from(Auftrag.class);
a. fetch (Auftrag_.auftragsPositionen). fetch (AuftragsPosition_.artikel);
…
![Page 21: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/21.jpg)
Basic Attribute Loading
Fetch-Strategie auch für einfache Werte wählbar
Lazy Loading sinnvoll bei
selten genutzten Werten
umfangreichen Daten
21
@Basic( fetch = FetchType.LAZY )private String longAdditionalInfo;
![Page 22: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/22.jpg)
Basic Attribute Loading
Messergebnis
Lesen von Kunden
10 'ungenutzte' Strings à 150 chars
1000 Interationen, EclipseLink, Oracle
22
EAGER LAZY
7.204 ms 6.820 ms -5 %
= Default-Einstellung
![Page 23: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/23.jpg)
Lazy-Load-Verfahren
Proxy
23
@OneToManyprivate Set<Auftrag> auftraege
get(…)
?DB
![Page 24: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/24.jpg)
Lazy-Load-Verfahren
Instrumentierung
24
@Basic(fetch = FetchType.LAZY)private String longAdditionalInfo;
get(…)?
DB
![Page 25: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/25.jpg)
Bytecode-Instrumentierung
25
� = Standard
� = Providerspezifische Konfiguration erforderlich
EclipselinkVerfahren SE EE
@Basic Entity Instrumentation � �
@xxxToOne Entity Instrumentation � �
@xxxToMany Collection Proxy � �
HibernateVerfahren SE EE
@Basic Entity Instrumentation � �
@xxxToOne Attribute Proxy � �
@xxxToMany Collection Proxy � �
![Page 26: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/26.jpg)
JPA Provider
Caching
26
EntityManager
DB
2nd
LevelCache
1st
LevelCache
QueryCache
![Page 27: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/27.jpg)
First Level Cache
Standard
Je EntityManager
Enthält in Sitzung geladene Objekte
Achtung: Speicherbedarf!
ggf. explizit entlasten (clear , detach )
27
EntityManager
1st
LevelCache
![Page 28: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/28.jpg)
First Level Cache
Arbeitetsitzungs-bezogen
28
// Kunden mit bestimmter Id ladenEntityManager em1 = emf.createEntityManager();Kunde k1 = em1.find(Kunde.class, id);
// Gleichen Kunden in 2. Session verändernEntityManager em2 = emf.createEntityManager();em2.getTransaction().begin();Kunde k2 = em2.find(Kunde.class, id);k2.setName("…");em2.getTransaction().commit();
// Gleichen Kunden in 1. Session erneut ladenKunde k3 = em1.find(Kunde.class, id);// ist unverändert!
![Page 29: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/29.jpg)
First Level Cache
HashMap-Semantik
benötigt Key
wird für Queries nicht benutzt
29
// Kunden mit bestimmter Id ladenEntityManager 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();
![Page 30: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/30.jpg)
Query Cache
Provider-spezifisch
Speichert Result Set IDs zu Queries
30
TypedQuery<Kunde> query= em.createQuery( "select k from Kunde k where k.name=:name" ,
Kunde.class);query.setParameter("name", "OPQ GbR" );… // Query Cache einschaltenKunde kunde = query.getSingleResult();
["select k from Kunde k where k.name=:name" , "OPQ GbR" ] � [id1 ]
![Page 31: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/31.jpg)
Query Cache
Trotz mehrfacher Querynur ein DB-Zugriff
31
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();…
}
![Page 32: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/32.jpg)
Query Cache
EclipseLink
Hibernate:
(Aktivierung in der Konfiguration notwendig)
32
TypedQuery<Kunde> query = em.createQuery(…);query.setHint("org.hibernate.cacheable", true);…
TypedQuery<Kunde> query = em.createQuery(…);query.setHint("eclipselink.cache-usage",
"CheckCacheThenDatabase");…
![Page 33: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/33.jpg)
Second Level Cache
JPA 2.0 unterstützt 2nd Level Cache
nur rudimentäre Konfiguration
ProviderspezifischeKonfigurationin der Praxisunabdingbar
33
JPA ProviderEntityManager
2nd
LevelCache
1st
LevelCache
![Page 34: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/34.jpg)
Second Level Cache
Providerspezifische Implementierung
Cache-Provider Infinispan, EHCache, OSCache, …
Cache-Strategienread-only, read-write, …
StorageMemory, Disk, Cluster, …
34
![Page 35: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/35.jpg)
Second Level Cache
Wirkt applikationsweit
Semantik ähnlich HashMap
Ladereihenfolge:
1st Level Cache (EntityManager)
2nd Level Cache, falls enabled
DB
35
![Page 36: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/36.jpg)
Second Level Cache
Vorteil bei häufig genutzten Daten
Konstanten
selten veränderte Daten
nur von dieser Anwendung veränderte Daten
36
![Page 37: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/37.jpg)
Second Level Cache
Bsp.: Stammdaten-Entity Land
wird n:1 von Kundereferenziert
nur wenige Land -Werte
Länder ändern sich nahezu nie
Länder können dauerhaft im Cache verbleiben
37
Kunde
Land
1*
![Page 38: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/38.jpg)
Second Level Cache
Konfiguration lt. Spec
38
<persistence-unit name="…"><provider>…</provider><shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>…
Cache aktiv für …
ALL alle Entities
NONE keine Klasse
ENABLE_SELECTIVE nur @Cacheable(true)
DISABLE_SELECTIVE alle außer @Cacheable(false)
@Entity@Cacheable(true)public class Land{
…
![Page 39: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/39.jpg)
Second Level Cache
EclipseLink
Default: DISABLE_SELECTIVE
Hibernate bis Version 3.x
ignoriert Standard-Konfig
benötigt eigene Annotation
39
@Entity@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)public class Land{
…
![Page 40: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/40.jpg)
Second Level Cache
Messergebnis(1000 Interationen, EclipseLink, Oracle)
ohne 2nd Level Cache: 10.883 msmit 2nd Level Cache für Land: 6.549 ms
40
![Page 41: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/41.jpg)
Paginierung
Queries mit großer Ergebnismenge'häppchenweise' verarbeiten
41
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 Artikelwhere … and rownum>=50 and rownum<60
![Page 42: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/42.jpg)
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
42
![Page 43: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/43.jpg)
Inheritance
Mehrere Abbildungen denkbar:
Alles in einer Tabelle
Eine Tabelle pro Klasse
Eine Tabelle pro konkreter Klasse
Strategie-Auswahl mit @Inheritance
43
<abstract>
Vehicle
Car Ship
![Page 44: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/44.jpg)
Inheritance
SINGLE_TABLE
44
@Entity@Inheritance(strategy=InheritanceType.SINGLE_TABLE)public abstract class Vehicle{
…
![Page 45: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/45.jpg)
Inheritance
JOINED
45
@Entity@Inheritance(strategy=InheritanceType.JOINED)public abstract class Vehicle{
…
![Page 46: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/46.jpg)
Inheritance
TABLE_PER_CLASS
46
@Entity@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)public abstract class Vehicle{
…
![Page 47: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/47.jpg)
Inheritance
Laufzeitvergleich für Queries
auf Basisklasse
auf abgeleitete Klasse
(1000 Iterationen, Ergebnis ca. 100 Einträge, Hibernate, MySQL)
47
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
![Page 48: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/48.jpg)
Inheritance
Optimale Performanz liefernSINGLE_TABLEund TABLE_PER_CLASS
Aber: Auch andere Implikationen
Genaue Analyse notwendig
48
![Page 49: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/49.jpg)
Providerabhängiges
Batch Size
Lazy-Varianten
Cache-Strategien
Prepared Statement Cache
49
![Page 50: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/50.jpg)
Providerabhängiges
Load Groups
Change Detection
DB Dialects
…
50
![Page 51: Optimierung von JPA-Anwendungen](https://reader034.vdocuments.pub/reader034/viewer/2022052304/558a1993d8b42a91448b4668/html5/thumbnails/51.jpg)
Fazit
Viele Optimierungen providerunabhängig möglich
Wesentlich:
Lazy Loading
Caching
Genaue Analyse notwendig
Messen
Kein Selbstzweck
51