Java/JPA

[API 개발 고급] ❗지연 로딩과 조회 성능 최적화(V3) 3/4

Lea Hwang 2022. 5. 25. 19:16

간단한 주문 조회 V3: 엔티티를 DTO로 변환 (fetch join 사용 O)

  결론 : fetch join으로 쿼리 1번 호출

 

 

 

 

V2에서 발생한 성능 문제(N+1)를 해결하기 위해서 

페치 조인을 사용해보겠습니다.

 

❗ [중요] ❗
실무에서 JPA 성능 문제의 90%는 N+1에서 발생하고
페치 조인은 성능 최적화를 위해서 자주 사용하기때문에 100% 이해하고 있어야 합니다.

 

 

OrderSimpleApiController

package jpabook.jpashop.api;


/**
 * @XToOne 관계(ManyToOne, OneToOne에서의 성능최적화)
 * Order
 * Order -> Member
 * Order -> Delivery
 */
@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {

    private final OrderRepository orderRepository;


    @GetMapping("/api/v3/simple-orders")
    public List<SimpleOrderDto> ordersV3() {
        List<Order> orders = orderRepository.findAllWithMemberDelivery();
        return orders.stream()
                .map(o -> new SimpleOrderDto(o))
                .collect(Collectors.toList());
    }

    @Data
    static class SimpleOrderDto {
        private Long orderId;
        private String name;
        private LocalDateTime orderDate;
        private OrderStatus orderStatus;
        private Address address; //배송지 주소

        //생성자
        public SimpleOrderDto(Order order) {
            orderId = order.getId();
            name = order.getMember().getName(); //LAZY 초기화
            orderDate = order.getOrderDate();
            orderStatus = order.getStatus();
            address = order.getDelivery().getAddress(); //LAZY 초기화
        }
    }

}

 

 

OrderRepository 추가

 /**
 * 지연 로딩과 조회 성능 최적화(V3) - 페치 조인
 *   지연 로딩, 프록시 무시하고 진짜 객체의 값을 채워서 가져옴
 */
public List<Order> findAllWithMemberDelivery() {
    return em.createQuery(
                    "select o from Order o" +
                            " join fetch o.member m" +
                            " join fetch o.delivery d", Order.class)
            .getResultList();
}

 

 

Postman확인

V2와 V3는 결과는 같지만 쿼리 수가 다릅니다.

 

 

V3에서는

쿼리가 1번 나간 것을 확인할 수 있습니다.

/* select o from Order o join fetch o.member m join fetch o.delivery d */ 
select 
    order0_.order_id as order_id1_6_0_, 
    member1_.member_id as member_i1_4_1_, 
    delivery2_.delivery_id as delivery1_2_2_, 
    order0_.delivery_id as delivery4_6_0_, 
    order0_.member_id as member_i5_6_0_, 
    order0_.order_date as order_da2_6_0_, 
    order0_.status as status3_6_0_, 
    member1_.city as city2_4_1_, 
    member1_.street as street3_4_1_, 
    member1_.zipcode as zipcode4_4_1_, 
    member1_.name as name5_4_1_, 
    delivery2_.city as city2_2_2_, 
    delivery2_.street as street3_2_2_, 
    delivery2_.zipcode as zipcode4_2_2_, 
    delivery2_.status as status5_2_2_ 
from 
    orders order0_ 
inner join 
    member member1_ 
        on order0_.member_id=member1_.member_id 
inner join 
    delivery delivery2_ 
        on order0_.delivery_id=delivery2_.delivery_id

 

 

 

 

 

정리

엔티티를 페치 조인(fetch join)을 사용해서 쿼리 1번에 조회

페치 조인으로 order -> member , order -> delivery는 이미 조회된 상태 이므로 지연 로딩 X

페치 조인은 실무에서 자주 사용하므로 꼭 알고 있어야 합니다.