Upload
simon-martinelli
View
730
Download
0
Embed Size (px)
Citation preview
PERFORMANTE JAVA ENTERPRISE APPLIKATIONENTROTZ 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 Entityorg.hibernate.HibernateException: cannot simultaneously fetch multiple bags
• JOIN FETCH– Achtung vor kartesischen Produkten
DTO
Quelle: Martin Fowler, http://martinfowler.com/eaaCatalog/dataTransferObject.html
CustomerInfoDTOpublic 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 EXPRESSIONQuery 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)}) })
QLRMQuery 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
jOOQList<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-GENERATORRandomDataGenerator randomDataGenerator = new RandomDataGenerator();
List<Customer> customers = randomDataGenerator.generateList(400,new GenConfig()
.name(Name.Firstname, "firstname") .name(Name.Lastname, "lastname"),
Customer.class);
LOG4JDBC
log4jdbc-log4j2: https://code.google.com/p/log4jdbc-log4j2/
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"/>