PERFORMANTE 
JAVA ENTERPRISE APPLIKATIONEN 
TROTZ O/R-MAPPING 
Simon Martinelli, simas GmbH 
@simas_ch | about.me/simas_ch 
https://github.com/simasch/orders
DAS PROBLEM
DAS MODELL
N+1 SELECT PROBLEM 
• Orders und OrderItems = FetchType.LAZY 
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = 
true) 
private Set<Order> orders; 
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true) 
private List<OrderItem> items; 
• Ein Query für alle Customers 
• Pro Customer 1 Query für die Orders 
• Pro Order 1 Query für die OrderItems
LÖSUNGSANSÄTZE 
• FetchType.EAGER oder EntityGraph (JPA 2.1) 
– Nur ein Hinweis für JPA 
– != SQL JOIN 
– Hibernate erlaubt nur eine List mit 
FetchType.EAGER pro Entity 
org.hibernate.HibernateException: cannot simultaneously fetch 
multiple bags 
• JOIN FETCH 
– Achtung vor kartesischen Produkten
DTO 
Quelle: Martin Fowler, http://martinfowler.com/eaaCatalog/dataTransferObject.html
CustomerInfoDTO 
public class CustomerInfoDTO { 
private final Long id; 
private final String lastname; 
private final String firstname; 
private final double revenue; 
public CustomerInfoDTO(Long id, String lastname, String firstname, 
double revenue) { 
this.id = id; 
this.lastname = lastname; 
this.firstname = firstname; 
this.revenue = revenue; 
} 
...
CONSTRUCTOR EXPRESSION 
Query q = em.createQuery( 
"SELECT " + 
"NEW control.CustomerInfoDTO(c.id, c.lastname, c.firstname," + 
"SUM(i.product.price)) " + 
"FROM Customer c " + 
"JOIN c.orders o " + 
"JOIN o.items i " + 
"GROUP BY c.lastnamem, c.firstname" + 
"ORDER BY c.lastname, c.firstname"); 
List<CustomerInfoDTO> list = q.getResultList();
DAS DATENMODELL IM ZENTRUM 
• Ein 1-1 Abbild der Datenbank in Entities oft 
unnötig 
X
WAS IST MIT JPQL OHNE DIE 
BEZIEHUNG CUSTOMER->ORDER? 
NEU IN JPA 2.1 
Query q = em.createQuery( 
"SELECT " + 
"NEW control.CustomerInfoDTO(c.id, c.lastname, c.firstname, " + 
"SUM(i.product.price)) " + 
"FROM Customer c " + 
"JOIN c.orders o ON o.customerId = c.id " + 
"JOIN o.items i " + 
"GROUP BY c.lastname, c.firstname" + 
"ORDER BY c.lastname, c.firstname"); 
List<CustomerInfoDTO> list = q.getResultList();
ORM FÜR ALLES? 
Just because you're using Hibernate, doesn't 
mean you have to use it for everything. 
A point I've been making for about ten years 
now. 
Gavin King, 10.12.2013
SQL? 
• VORTEILE 
– Testbar mit SQL Developer/TOAD/IDE 
– Optimierbar (Execution Plan) 
• ABER SQL VON HAND? NEIN! 
– JPA 2.1 ConstructorResult 
– QLRM 
– jOOQ
JPA 2.1 CONSTRUCTOR RESULT 
Query q = em.createNativeQuery( 
"SELECT C.ID, C.LASTNAME, C.FIRSTNAME, SUM(P.PRICE) AS REVENUE" + 
"FROM CUSTOMERS C " + 
"JOIN ORDERS O ON O.CUSTOMER_ID = C.ID " + 
"JOIN ORDERITEMS I ON I.ORDER_ID = O.ID " + 
"JOIN PRODUCTS P ON P.ID = I.PRODUCT_ID " + 
"GROUP BY C.ID, C.LASTNAME, C.FIRSTNAME " + 
"ORDER BY C.LASTNAME, C.FIRSTNAME", "CustomerInfoDTO"); 
@SqlResultSetMapping(name="CustomerInfoDTO", 
classes={ 
@ConstructorResult(targetClass=CustomerInfoDTO.class, 
columns={@ColumnResult(name="ID"), 
@ColumnResult(name="LASTNAME"), 
@ColumnResult(name="FIRSTNAME"), 
@ColumnResult(name="REVENUE", type=Double.class)}) 
})
QLRM 
Query q = em.createNativeQuery( 
"SELECT C.ID, C.LASTNAME, C.FIRSTNAME, SUM(P.PRICE) AS REVENUE" + 
"FROM CUSTOMERS C " + 
"JOIN ORDERS O ON O.CUSTOMER_ID = C.ID " + 
"JOIN ORDERITEMS I ON I.ORDER_ID = O.ID " + 
"JOIN PRODUCTS P ON P.ID = I.PRODUCT_ID " + 
"GROUP BY C.ID, C.LASTNAME, C.FIRSTNAME " + 
"ORDER BY C.LASTNAME, C.FIRSTNAME"); 
JpaResultMapper mapper = new JpaResultMapper(); 
List<CustomerInfoDTO> list = 
jpaResultMapper.list(q, CustomerInfoDTO.class); 
QLRM: https://github.com/simasch/qlrm
jOOQ 
List<CustomersInfoDTO> list = create. 
select(CUSTOMERS.ID, CUSTOMERS.LASTNAME, CUSTOMERS.FIRSTNAME, 
sum(PRODUCTS.PRICE)). 
from(CUSTOMERS). 
join(ORDERS).on(ORDERS.CUSTOMER_ID.eq(CUSTOMERS.ID)). 
join(ORDERITEMS).on(ORDERITEMS.ORDER_ID.eq(ORDERS.ID)). 
join(PRODUCTS).on(PRODUCTS.ID.eq(ORDERITEMS.PRODUCT_ID)). 
groupBy(CUSTOMERS.ID, CUSTOMERS.NAME). 
orderBy(CUSTOMERS.NAME). 
fetchInto(CustomersInfoDTO.class); 
jOOQ: http://www.jooq.org
CQRS 
Quelle: Martin Fowler, http://martinfowler.com/bliki/CQRS.html
CQRS IM KLEINEN 
• QUERIES 
– JPA Constructor Expression 
– JPA ConstructorResult 
– QLRM 
– jOOQ 
• COMMANDS 
– JPA Entities
EMPFEHLUNGEN 
• Entities zum Erstellen und Ändern der Daten 
• DTO für lesende Zugriffe 
– Constructor Expression 
– SQL mit QLRM oder jOOQ
BONUS MATERIAL
RANDOM-DATA-GENERATOR 
RandomDataGenerator randomDataGenerator = new RandomDataGenerator(); 
List<Customer> customers = randomDataGenerator.generateList( 
400, 
new GenConfig() 
.name(Name.Firstname, "firstname") 
.name(Name.Lastname, "lastname"), 
Customer.class);
LOG4JDBC 
Anpassungen im META-INF/persistence.xml 
<property name="javax.persistence.jdbc.url" 
value="jdbc:log4jdbc:derby://localhost:1527/orders"/> 
<property name="javax.persistence.jdbc.driver" 
value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"/> 
log4jdbc-log4j2: https://code.google.com/p/log4jdbc-log4j2/

Performante Java Enterprise Applikationen trotz O/R-Mapping

  • 1.
    PERFORMANTE JAVA ENTERPRISEAPPLIKATIONEN TROTZ O/R-MAPPING Simon Martinelli, simas GmbH @simas_ch | about.me/simas_ch https://github.com/simasch/orders
  • 2.
  • 3.
  • 4.
    N+1 SELECT PROBLEM • Orders und OrderItems = FetchType.LAZY @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true) private Set<Order> orders; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true) private List<OrderItem> items; • Ein Query für alle Customers • Pro Customer 1 Query für die Orders • Pro Order 1 Query für die OrderItems
  • 5.
    LÖSUNGSANSÄTZE • FetchType.EAGERoder EntityGraph (JPA 2.1) – Nur ein Hinweis für JPA – != SQL JOIN – Hibernate erlaubt nur eine List mit FetchType.EAGER pro Entity org.hibernate.HibernateException: cannot simultaneously fetch multiple bags • JOIN FETCH – Achtung vor kartesischen Produkten
  • 6.
    DTO Quelle: MartinFowler, http://martinfowler.com/eaaCatalog/dataTransferObject.html
  • 7.
    CustomerInfoDTO public classCustomerInfoDTO { private final Long id; private final String lastname; private final String firstname; private final double revenue; public CustomerInfoDTO(Long id, String lastname, String firstname, double revenue) { this.id = id; this.lastname = lastname; this.firstname = firstname; this.revenue = revenue; } ...
  • 8.
    CONSTRUCTOR EXPRESSION Queryq = em.createQuery( "SELECT " + "NEW control.CustomerInfoDTO(c.id, c.lastname, c.firstname," + "SUM(i.product.price)) " + "FROM Customer c " + "JOIN c.orders o " + "JOIN o.items i " + "GROUP BY c.lastnamem, c.firstname" + "ORDER BY c.lastname, c.firstname"); List<CustomerInfoDTO> list = q.getResultList();
  • 9.
    DAS DATENMODELL IMZENTRUM • Ein 1-1 Abbild der Datenbank in Entities oft unnötig X
  • 10.
    WAS IST MITJPQL OHNE DIE BEZIEHUNG CUSTOMER->ORDER? NEU IN JPA 2.1 Query q = em.createQuery( "SELECT " + "NEW control.CustomerInfoDTO(c.id, c.lastname, c.firstname, " + "SUM(i.product.price)) " + "FROM Customer c " + "JOIN c.orders o ON o.customerId = c.id " + "JOIN o.items i " + "GROUP BY c.lastname, c.firstname" + "ORDER BY c.lastname, c.firstname"); List<CustomerInfoDTO> list = q.getResultList();
  • 11.
    ORM FÜR ALLES? Just because you're using Hibernate, doesn't mean you have to use it for everything. A point I've been making for about ten years now. Gavin King, 10.12.2013
  • 12.
    SQL? • VORTEILE – Testbar mit SQL Developer/TOAD/IDE – Optimierbar (Execution Plan) • ABER SQL VON HAND? NEIN! – JPA 2.1 ConstructorResult – QLRM – jOOQ
  • 13.
    JPA 2.1 CONSTRUCTORRESULT Query q = em.createNativeQuery( "SELECT C.ID, C.LASTNAME, C.FIRSTNAME, SUM(P.PRICE) AS REVENUE" + "FROM CUSTOMERS C " + "JOIN ORDERS O ON O.CUSTOMER_ID = C.ID " + "JOIN ORDERITEMS I ON I.ORDER_ID = O.ID " + "JOIN PRODUCTS P ON P.ID = I.PRODUCT_ID " + "GROUP BY C.ID, C.LASTNAME, C.FIRSTNAME " + "ORDER BY C.LASTNAME, C.FIRSTNAME", "CustomerInfoDTO"); @SqlResultSetMapping(name="CustomerInfoDTO", classes={ @ConstructorResult(targetClass=CustomerInfoDTO.class, columns={@ColumnResult(name="ID"), @ColumnResult(name="LASTNAME"), @ColumnResult(name="FIRSTNAME"), @ColumnResult(name="REVENUE", type=Double.class)}) })
  • 14.
    QLRM Query q= em.createNativeQuery( "SELECT C.ID, C.LASTNAME, C.FIRSTNAME, SUM(P.PRICE) AS REVENUE" + "FROM CUSTOMERS C " + "JOIN ORDERS O ON O.CUSTOMER_ID = C.ID " + "JOIN ORDERITEMS I ON I.ORDER_ID = O.ID " + "JOIN PRODUCTS P ON P.ID = I.PRODUCT_ID " + "GROUP BY C.ID, C.LASTNAME, C.FIRSTNAME " + "ORDER BY C.LASTNAME, C.FIRSTNAME"); JpaResultMapper mapper = new JpaResultMapper(); List<CustomerInfoDTO> list = jpaResultMapper.list(q, CustomerInfoDTO.class); QLRM: https://github.com/simasch/qlrm
  • 15.
    jOOQ List<CustomersInfoDTO> list= create. select(CUSTOMERS.ID, CUSTOMERS.LASTNAME, CUSTOMERS.FIRSTNAME, sum(PRODUCTS.PRICE)). from(CUSTOMERS). join(ORDERS).on(ORDERS.CUSTOMER_ID.eq(CUSTOMERS.ID)). join(ORDERITEMS).on(ORDERITEMS.ORDER_ID.eq(ORDERS.ID)). join(PRODUCTS).on(PRODUCTS.ID.eq(ORDERITEMS.PRODUCT_ID)). groupBy(CUSTOMERS.ID, CUSTOMERS.NAME). orderBy(CUSTOMERS.NAME). fetchInto(CustomersInfoDTO.class); jOOQ: http://www.jooq.org
  • 16.
    CQRS Quelle: MartinFowler, http://martinfowler.com/bliki/CQRS.html
  • 17.
    CQRS IM KLEINEN • QUERIES – JPA Constructor Expression – JPA ConstructorResult – QLRM – jOOQ • COMMANDS – JPA Entities
  • 18.
    EMPFEHLUNGEN • Entitieszum Erstellen und Ändern der Daten • DTO für lesende Zugriffe – Constructor Expression – SQL mit QLRM oder jOOQ
  • 19.
  • 20.
    RANDOM-DATA-GENERATOR RandomDataGenerator randomDataGenerator= new RandomDataGenerator(); List<Customer> customers = randomDataGenerator.generateList( 400, new GenConfig() .name(Name.Firstname, "firstname") .name(Name.Lastname, "lastname"), Customer.class);
  • 21.
    LOG4JDBC Anpassungen imMETA-INF/persistence.xml <property name="javax.persistence.jdbc.url" value="jdbc:log4jdbc:derby://localhost:1527/orders"/> <property name="javax.persistence.jdbc.driver" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"/> log4jdbc-log4j2: https://code.google.com/p/log4jdbc-log4j2/