mastering jpa performance - java forum stuttgart · das problem 2 6. juli 2017 java forum stuttgart...

50
BASEL BERN BRUGG DÜSSELDORF FRANKFURT A.M. FREIBURG I.BR. GENEVA HAMBURG COPENHAGEN LAUSANNE MUNICH STUTTGART VIENNA ZURICH Mastering JPA Performance Thomas Bröll Principal Consultant Trivadis GmbH, Stuttgart

Upload: lytruc

Post on 23-Feb-2019

226 views

Category:

Documents


0 download

TRANSCRIPT

BASEL BERN BRUGG DÜSSELDORF FRANKFURT A.M. FREIBURG I.BR. GENEVA

HAMBURG COPENHAGEN LAUSANNE MUNICH STUTTGART VIENNA ZURICH

Mastering JPA Performance

Thomas BröllPrincipal ConsultantTrivadis GmbH, Stuttgart

Das Problem

Java Forum Stuttgart - Mastering JPA Performance2 6. Juli 2017

Langsame Anwendung, Java, Datenbankzugriffe mit JPA/Hibernate

Entwickler: Geringe Auslastung am Applikationsserver

– Die Datenbank ist das Problem!

DBA: Geringe Auslastung der Datenbank

– Die Datenbank ist nicht das Problem!

Netzwerk auch nicht am Limit!

DatabaseApplication

Server

Clients

(Web)

Test Szenario

Java Forum Stuttgart - Mastering JPA Performance3 6. Juli 2017

Schreibe Daten aus CSV

Kleine Objekte, 3 Ebenen

Keine Optimierungen

Lokale in-Memory-Datenbank (javadb)

Spring Boot

State

City

Postal

Code

1

1

n

n

13381

10585

16

Java Forum Stuttgart - Mastering JPA Performance4 6. Juli 2017

Schreiben

Java Code

Java Forum Stuttgart - Mastering JPA Performance5 6. Juli 2017

City c = …em.persist(c);

Service

@Entitypublic class City {@GeneratedValue @Id long id; …

Entity

Code – ?? Sekunden

Java Forum Stuttgart - Mastering JPA Performance6 6. Juli 2017

StreamSupport.stream(CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(fr).spliterator(), false).map(record -> {

State state = intern (new State(record.get("bundesland")), states, State::getName, s -> {em.persist(s);

});City city = intern (new City(record.get("ort")), cities, City::getName, c -> {

c.setState(state);em.persist(c);

});

PostalCode postalCode = new PostalCode(record.get("plz"));postalCode.setCity(city);return new ImmutableTriple<>(state, city, postalCode);

})//.forEach(triple -> {

em.persist(triple.getRight());});

Java Forum Stuttgart - Mastering JPA Performance7 6. Juli 2017

Problemanalyse

(allgemein)

Aus Sicht der Datenbank

Java Forum Stuttgart - Mastering JPA Performance8 6. Juli 2017

DB Monitoring

SQL Statements & Transaktionen

CPU

I/O

Top 10 statements (Anzahl)

Top 10 statements (Ausführungszeit)

Oracle Workload repository report

Java Forum Stuttgart - Mastering JPA Performance9 6. Juli 2017

Aus Sicht der Java VM

Java Forum Stuttgart - Mastering JPA Performance10 6. Juli 2017

Java VM

JVisualVM / jstack

JDBC Monitoring

JVM Profiling

Total/used memory

Methoden hotspots (Ausführungszeit)

Logging

JVM Analyse 1

Java Forum Stuttgart - Mastering JPA Performance11 6. Juli 2017

"main" #1 prio=5 os_prio=0 tid=0x00000000023ce800 nid=0x1534 runnable [0x000000000266c000]

java.lang.Thread.State: RUNNABLE

at java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)

at java.net.SocketInputStream.read(SocketInputStream.java:170)

at java.net.SocketInputStream.read(SocketInputStream.java:141)

at com.mysql.cj.core.io.ReadAheadInputStream.fill(ReadAheadInputStream.java:101)at com.mysql.cj.core.io.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:144)

at com.mysql.cj.core.io.ReadAheadInputStream.read(ReadAheadInputStream.java:174)

- locked <0x00000006c5bb1b58> (a com.mysql.cj.core.io.ReadAheadInputStream)

at java.io.FilterInputStream.read(FilterInputStream.java:133)

at com.mysql.cj.core.io.FullReadInputStream.readFully(FullReadInputStream.java:58)

at com.mysql.cj.mysqla.io.SimplePacketReader.readHeader(SimplePacketReader.java:60)

at com.mysql.cj.mysqla.io.TimeTrackingPacketReader.readHeader(TimeTrackingPacketReader.java:48)

at com.mysql.cj.mysqla.io.MultiPacketReader.readHeader(MultiPacketReader.java:51)

at com.mysql.cj.mysqla.io.MysqlaProtocol.readPacket(MysqlaProtocol.java:521)

at com.mysql.cj.mysqla.io.MysqlaProtocol.checkErrorPacket(MysqlaProtocol.java:723)

at com.mysql.cj.mysqla.io.MysqlaProtocol.sendCommand(MysqlaProtocol.java:662)

at com.mysql.cj.mysqla.io.MysqlaProtocol.sqlQueryDirect(MysqlaProtocol.java:950)

at com.mysql.cj.mysqla.MysqlaSession.sqlQueryDirect(MysqlaSession.java:431)

at com.mysql.cj.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:1974)

- locked <0x00000006c5b76500> (a com.mysql.cj.jdbc.ConnectionImpl)

at com.mysql.cj.jdbc.ConnectionImpl.commit(ConnectionImpl.java:1196)

- locked <0x00000006c5b76500> (a com.mysql.cj.jdbc.ConnectionImpl)

at sun.reflect.GeneratedMethodAccessor38.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:126)

at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:108)

at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:81)

at com.sun.proxy.$Proxy86.commit(Unknown Source)

at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.delegateWork(JdbcIsolationDelegate.java:59)

at

org.hibernate.id.enhanced.TableGenerator$1.getNextValue(TableGenerator.java:530)at org.hibernate.id.enhanced.NoopOptimizer.generate(NoopOptimizer.java:40)

at org.hibernate.id.enhanced.TableGenerator.generate(TableGenerator.java:526)

at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:101)

at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67)

at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189)

at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132)

at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58)

at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:775)

at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748)

at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753)

at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1146)

at sun.reflect.GeneratedMethodAccessor37.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)

JVM stack (7 von 10)

Lösung: * Multi ID Generator

Java Forum Stuttgart - Mastering JPA Performance12 6. Juli 2017

Auto-ID Generator erzeugt viele Abfragen

Transaktion wartet synchron

Databank-generierte ID-Spalten

– Sind keine Lösung (JDBC Limit)

Multi ID Generator!

– Ungenutzte IDs

– Keine streng monotone Vergabe der Ids mehr

UUID-Generator ?

@TableGenerator(name = "city", initialValue = 1000, allocationSize = Constants.ALLOCATION_SIZE, table = "sequences", pkColumnName = "name", valueColumnName = "value")@GeneratedValue(generator="city")@Id long id;

Table Generator mit AllocationSize!

Java Forum Stuttgart - Mastering JPA Performance13 6. Juli 2017

0

2000

4000

6000

8000

10000

12000

14000

0

10000

20000

30000

40000

50000

60000

70000

80000

1 2 4 8 16

Allocation vs. time

Statements

time

Der gleiche Code – 6.2 Sekunden

Java Forum Stuttgart - Mastering JPA Performance14 6. Juli 2017

StreamSupport.stream(CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(fr).spliterator(), false).map(record -> {

State state = intern (new State(record.get("bundesland")), states, State::getName, s -> {em.persist(s);

});City city = intern (new City(record.get("ort")), cities, City::getName, c -> {

c.setState(state);em.persist(c);

});

PostalCode postalCode = new PostalCode(record.get("plz"));postalCode.setCity(city);return new ImmutableTriple<>(state, city, postalCode);

})//.forEach(triple -> {

em.persist(triple.getRight());});

Analyse 2

Java Forum Stuttgart - Mastering JPA Performance15 6. Juli 2017

"main" #1 prio=5 os_prio=0 tid=0x00000000017be800 nid=0x29fc runnable [0x000000000315c000]

java.lang.Thread.State: RUNNABLE

at java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)

at java.net.SocketInputStream.read(SocketInputStream.java:170)

at java.net.SocketInputStream.read(SocketInputStream.java:141)

at com.mysql.cj.core.io.ReadAheadInputStream.fill(ReadAheadInputStream.java:101)

at com.mysql.cj.core.io.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:144)

at com.mysql.cj.core.io.ReadAheadInputStream.read(ReadAheadInputStream.java:174)

- locked <0x00000006c5a99ea8> (a com.mysql.cj.core.io.ReadAheadInputStream)

at java.io.FilterInputStream.read(FilterInputStream.java:133)

at com.mysql.cj.core.io.FullReadInputStream.readFully(FullReadInputStream.java:58)at com.mysql.cj.mysqla.io.SimplePacketReader.readHeader(SimplePacketReader.java:60)

at com.mysql.cj.mysqla.io.TimeTrackingPacketReader.readHeader(TimeTrackingPacketReader.java:48)

at com.mysql.cj.mysqla.io.MultiPacketReader.readHeader(MultiPacketReader.java:51)

at com.mysql.cj.mysqla.io.MysqlaProtocol.readPacket(MysqlaProtocol.java:521)

at com.mysql.cj.mysqla.io.MysqlaProtocol.checkErrorPacket(MysqlaProtocol.java:723)

at com.mysql.cj.mysqla.io.MysqlaProtocol.sendCommand(MysqlaProtocol.java:662)

at com.mysql.cj.mysqla.io.MysqlaProtocol.sqlQueryDirect(MysqlaProtocol.java:950)

at com.mysql.cj.mysqla.MysqlaSession.sqlQueryDirect(MysqlaSession.java:431)

at com.mysql.cj.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:1978)

- locked <0x00000006c59fd458> (a com.mysql.cj.jdbc.ConnectionImpl)

at com.mysql.cj.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1826)

- locked <0x00000006c59fd458> (a com.mysql.cj.jdbc.ConnectionImpl)

at com.mysql.cj.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2034)

- locked <0x00000006c59fd458> (a com.mysql.cj.jdbc.ConnectionImpl)

at com.mysql.cj.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:1970)

- locked <0x00000006c59fd458> (a com.mysql.cj.jdbc.ConnectionImpl)

at com.mysql.cj.jdbc.PreparedStatement.executeLargeUpdate(PreparedStatement.java:4984)

at com.mysql.cj.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1955)

at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)

at

org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonB

atchingBatch.java:45)at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2897)

at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3397)

at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:89)

at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:582)

at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456)

at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)

at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)

JVM stack

Batch Optimierung

Java Forum Stuttgart - Mastering JPA Performance16 6. Juli 2017

spring.jpa.properties.hibernate.jdbc.batch_size=100

Aktiviere JDBC Batch

0

1000

2000

3000

4000

5000

1 2 4 8 16

Batch size vs. time

Time

Batch optimierter Code – 1.9 Sekunden!!

Java Forum Stuttgart - Mastering JPA Performance17 6. Juli 2017

List<ImmutableTriple<State,City,PostalCode>> triples = StreamSupport.stream(CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(fr).spliterator(), false).map(record -> {

State state = intern (new State(record.get("bundesland")), states, State::getName, s -> {em.persist(s);

});City city = intern (new City(record.get("ort")), cities, City::getName, c -> {

c.setState(state);em.persist(c);

});

PostalCode postalCode = new PostalCode(record.get("plz"));postalCode.setCity(city);return new ImmutableTriple<>(state, city, postalCode);

})//.collect(Collectors.toList());

triples.stream().forEach(triple -> em.persist(triple.getRight()));

Gebrochene JDBC batches

Java Forum Stuttgart - Mastering JPA Performance18 6. Juli 2017

Bester Fall: 3 JDBC batches

States brechen Batch für City 16 *

Cities brechen PostalCode 10585 *

16 Unterbrechungen haben keinen

signifikanten Einfluss, aber die 10585!

1:n Beziehung ist nahezu 1:1

State

City

Postal

Code

1

1

n

n

13381

10585

16

Oracle Workload repository report

Java Forum Stuttgart - Mastering JPA Performance19 6. Juli 2017

Lösung: Batch Sortierung

Java Forum Stuttgart - Mastering JPA Performance20 6. Juli 2017

Sortiere (INSERT) Operationen für JDBC batch

Batch wird gebrochen durch

– Queries

– Flush()

– Persistieren einer anderen Entität

– Identity columns (database level)

Java Forum Stuttgart - Mastering JPA Performance21 6. Juli 2017

Updates

Entity Lifecycle

Java Forum Stuttgart - Mastering JPA Performance22 6. Juli 2017

...

city.setName(„newName“)

} // update & commit on exit

Automagisch? Optimiert?

Managed

RemovedDetached

New

Merge (Anti-Pattern)

Java Forum Stuttgart - Mastering JPA Performance23 6. Juli 2017

TestEntity te = …

TestEntity merged = em.merge(te);

Merge führt zuerst SELECT, dann UPDATE aus

SELECT – COPY – RETURN

https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Gui

de.html#pc-cascade-merge

Großer Persistence-Context

Java Forum Stuttgart - Mastering JPA Performance24 6. Juli 2017

Große Anzahl Instanzen/Attribute

EntityManager führt „dirty check“ für jeden flush() aus

Performance hängt von Implementierung ab

– Equals

– „Dirty“ Flag

(Anti-)Pattern: flush & clear (ok für große Mengen)

Java Forum Stuttgart - Mastering JPA Performance25 6. Juli 2017

em.clear()

Aufräumen – verwirft offene Änderungen

em.flush()

Schreibe alle Änderungen

Fremdschlüssel

Java Forum Stuttgart - Mastering JPA Performance26 6. Juli 2017

State s = em.getReference(State.class, 4711L);

City c = new City("Test getReference");c.setState(s);...

getReference führt keinen SELECT aus (verlässt sich auf referentielle Integrität)

Jeder Zugriff auf die Properties dieses Objekts führt zu einem SELECT auf die DB!

Java Forum Stuttgart - Mastering JPA Performance27 6. Juli 2017

Lesen von Daten

Eine Entität finden/laden

Java Forum Stuttgart - Mastering JPA Performance28 6. Juli 2017

City st = em.createNamedQuery("City.byName", City.class).setParameter("name", "Stuttgart").getSingleResult();

Query Ausführung

@Entity

@NamedQueries({@NamedQuery(name="City.byName", query="SELECT e FROM City e

WHERE e.name=:name")})

public class City { …

Query Definition

SQLs

Java Forum Stuttgart - Mastering JPA Performance29 6. Juli 2017

Hibernate: /* City.byName */ select city0_.id as id1_0_,

city0_.name as name2_0_, city0_.state_id as state_id3_0_ from city city0_ where

city0_.name=?Hibernate:

select state0_.id as id1_6_0_, state0_.name as name2_6_0_ from

state state0_ where

state0_.id=?

„1+n“-Problem

Java Forum Stuttgart - Mastering JPA Performance30 6. Juli 2017

EIN Statement aus der Anwendung

N Statements durch Hibernate

Objekte werden nachgeladen (n:1, 1:1)

Hierdurch entsteht indeterministisches Verhalten bzgl. der Laufzeit!

Wird teilweise auch durch das UI ausgelöst!

Lesezugriffe

Java Forum Stuttgart - Mastering JPA Performance31 6. Juli 2017

Sicht der Datenbank:

– „einfache“ Queries auf Tabellen, in der Regel alle Spalten

– Viele Abfragen auf Primärschlüssel

– Strukturell einfache Abfragen, viele Joins möglich

Collections (@OneToMany) sind Lazy

Referenzen auf Objekte sind (@ManyToOne)

– … lazy ist nur ein Hint!

Lazy (nur 1 Statement)

Java Forum Stuttgart - Mastering JPA Performance32 6. Juli 2017

/* City.byName */ select

city0_.id as id1_0_,

city0_.name as name2_0_,

city0_.state_id as state_id3_0_

from city city0_

where

city0_.name=?

@ManyToOne(fetch=FetchType.LAZY)State state;

In City

Eager (nur 1 Statement) – nicht für Abfragen!

Java Forum Stuttgart - Mastering JPA Performance33 6. Juli 2017

selectcity0_.id as id1_0_0_, city0_.name as name2_0_0_,city0_.state_id as state_id3_0_0_, state1_.id as id1_6_1_,state1_.name as name2_6_1_

from city city0_ left outer join state state1_ on city0_.state_id=state1_.id where city0_.id=?

@ManyToOne(fetch=FetchType.EAGER)State state;

In City

Lösung: Prefetch Query

Java Forum Stuttgart - Mastering JPA Performance34 6. Juli 2017

Eager & Lazy sind evtl. nicht ausreichend, Definition im Query möglich

Eager erzeugt große Ergebnismengen

Prefetch Query lädt abhängige Entitäten, nutzt den PersistenceContext als Cache

– Deterministische Anzahl SQL-Abfragen

– Kleinere Gesamtmenge (Zeilen & Spalten)

Beispiel: Lade mehrere Postleitzahlen mit Städten und BundesländernPrefetch: SELECT e.city.state FROM PostalCode e WHERE e.code IN (:codes)

Query: SELECT e FROM PostalCode e

JOIN FETCH e.city WHERE e.code IN (:codes)

Unabhängig von der Datenkonstellation immer exakt zwei SQL-Statements!

Lösung: Pagination

Java Forum Stuttgart - Mastering JPA Performance35 6. Juli 2017

Abhängig von der Implementierung (Datenbank-Dialekt)

Kleine Performance-Verbesserung

query.setFirstResult(0);

query.setMaxResults(10);

Reduziere die Gesamtergebnismenge

Java Forum Stuttgart - Mastering JPA Performance36 6. Juli 2017

In der Cloud ?

AppEngine: US, Database: US / Europe

Java Forum Stuttgart - Mastering JPA Performance38 6. Juli 2017

Database „ping“ - optimierter Code

Java Forum Stuttgart - Mastering JPA Performance39 6. Juli 2017

SELECT ohne Ergebnis, Roundtrip-Zeit

App Engine US-Central, MySQL Europe-West: 120ms (max: 215ms)

App Engine US-Central, MySQL US-Central: 1-2ms (max: 4-5ms)

Physikalisches Limit: 80ms (Lichtgeschwindigkeit in Glasfaser: 200.000 km/s)

Empfehlung: Mehr als eine Zone nutzen, aber in der gleichen Region bleiben

Cloud Messungen

Java Forum Stuttgart - Mastering JPA Performance40 6. Juli 2017

Scenario 10% (2456 rows) 100% (23.978 rows)

US->US 2.59s 15.9s

US->EU 264s = 4:24 ~43min

43min ?

Java Forum Stuttgart - Mastering JPA Performance41 6. Juli 2017

43 min / 110ms ~ 23.500

Der JDBC Treiber ...

Java Forum Stuttgart - Mastering JPA Performance42 6. Juli 2017

... Macht nicht unbedingt was wir ihm sagen

Optionen in der Dokumentation prüfen (herstellerspezifisch)

Konfigurationen bzgl. Performance testen

Der JDBC Treiber …

Java Forum Stuttgart - Mastering JPA Performance43 6. Juli 2017

Batch Update (mysql/mariadb)

Default:

mysql: rewriteBatchedStatements

mariadb: useBatchMultiSend (default=100)

JDBC Driver fetch size

Oracle: 10

mysql: *

Java Forum Stuttgart - Mastering JPA Performance44 6. Juli 2017

Mehr …

Top-Down in die Datenbank

Java Forum Stuttgart - Mastering JPA Performance45 6. Juli 2017

Queries erhalten nicht automatisch den benötigten Index

Fremdschlüssel-Index für Löschungen

Index-Optimierung (Fetch from Index) nur bedingt möglich

Anti-Pattern: CASCADE

Java Forum Stuttgart - Mastering JPA Performance46 6. Juli 2017

Bietet das automatische Kaskadieren von Operationen im EntityManager

Vermeiden oder nur begrenzt nutzen!

Rekursives Problem

Keine Kontrolle der Anwendung

Keine Optimierungen möglich (z.B. Batch-Optimierung)

Langfristig nicht kontrollierbar

Caching (Anti-Pattern)

Java Forum Stuttgart - Mastering JPA Performance47 6. Juli 2017

Nutze Caching, wenn

Daten häufig gelesen und

Selten verändert werden

Alte Daten akzeptabel sind

– Vor allem in Cloud-/Container-Umgebungen

Ausreichend Speicher verfügbar ist

JPA 2.2 !

Java Forum Stuttgart - Mastering JPA Performance48 6. Juli 2017

Streams für Query-Ergebnisse

CDI (Attribute Converter)

@NamedQuery - @Repeatable

Java 8 Date & Time

Fazit

Java Forum Stuttgart - Mastering JPA Performance49 6. Juli 2017

JPA funktioniert!

SQL Roundtrips für einzelne Objekte sind teuer

Den gesamten Technologiestack betrachten

Die Probleme betreffen meist nur 5% aller Fälle

Das kann aber 95% der gesamten Probleme ausmachen!

Empfehlung: Jeder Anwendungsfall soll sich deterministisch verhalten!

Bewusstsein für die Infrastruktur hilft, Probleme vorab einzuschätzen

Java aktuell

Java Forum Stuttgart - Mastering JPA Performance50 6. Juli 2017

Fragen und AntwortenThomas Bröll

Principal Consultant

Tel. +49 162 2733918

[email protected]

@tvdtkb

6. Juli 2017 Java Forum Stuttgart - Mastering JPA Performance51