Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JPA 的 N+1 試煉之路

JPA 的 N+1 試煉之路

Speaker: Tim Huang
Event: https://jcconf.tw/2022/

LINE Developers Taiwan

October 07, 2022
Tweet

More Decks by LINE Developers Taiwan

Other Decks in Technology

Transcript

  1. JPA provides an object relational mapping approach that you declaratively

    define how to map Java objects to relational database tables in a standard, portable way. Just a specification, doesn’t perform any operation. (Hibernate, EclipseLink Java Persistence API What is JPA Object Table
  2. One query for parents N queries for children What is

    the N+1 problem in JPA? parent parent query child child child query query
  3. Why N+1 problem will decrease performance? Round trips to the

    database cost more in overhead than the query itself.
  4. N+1 Problem 0 20 40 60 80 100 10 1000

    10000 50000 SQL Execution Time One Join Query N+1 Query (Seconds) (Total Parents)
  5. N+1 Problem customer id int[pk] name char order_history id int[pk]

    customer_id int price int8 created_time datetime 1 *
  6. We want to calculate the total price that orders are

    after a specific event day. (Using Spring Data JPA Repository + Hibernate) SQL generated ? customerRepository.findAll() .stream() .map(Customer::getOrderHistories) .flatMap(Collection::stream) .filter(this::isAfterEventDate) .mapToLong(OrderHistory::getPrice) .sum();
  7. customer id name 1 Tim 2 Sandy order_history id customer_id

    price created_time 1 1 100 2022-09-01 2 1 200 2022-10-05 3 2 300 2022-10-07 > customerRepository.findAll() [SQL] select * from customer Total Query Counts: 1
  8. customer id name 1 Tim 2 Sandy order_history id customer_id

    price created_time 1 1 100 2022-09-01 2 1 200 2022-10-05 3 2 300 2022-10-07 > customer.getOrderHistories() [SQL] select * from order_history where customer_id = 1 Total Query Counts: 2
  9. customer id name 1 Tim 2 Sandy order_history id customer_id

    price created_time 1 1 100 2022-09-01 2 1 200 2022-10-05 3 2 300 2022-10-07 > customer.getOrderHistories() [SQL] select * from order_history where customer_id = 2 Total Query Counts: 3
  10. JPA Fetch Type FetchType.LAZY FetchType.EAGER • @OneToMany • @ManyToMany •

    @OneToOne • @ManyToOne Why order histories relation lazy loading ?
  11. How can we solve the N+1 query problem TWO Queries

    JOIN FETCH ENTITY GRAPH Select * from parent Select * from child where id in (…)
  12. Entity Graph Entity Graph is then to improve the runtime

    performance when loading the entity’s related associations and basic fields. customer order history Switch between lazy and eager ? LAZY EAGER customer order history
  13. Entity Graph @NamedEntityGraph(name = {"customer.orderHistories"}, attributeNodes = { @NamedAttributeNode("orderHistories")}) public

    class Customer select * from customer c left outer join order_history oh on c.id = oh.customer_id public interface CustomerRepository extends JpaRepository<Customer, Long> { @Override @EntityGraph("customer.orderHistories") List<Customer> findAll(); }
  14. Reference • JPA Entity Graph (Spring Data JPA Doc) •

    Java Persistence API (oracle Java EE 6 Tutorial) • Hibernate ORM (Your relational data. Objectively) • Introduction_to_Java_Persistence(eclipse wiki)