[Spring Boot2][3] 2. API 개발 고급 - 지연 로딩과 조회 성능 최적화(2)

🏷 간단한 주문 조회 V3 : 엔티티를 DTO로 변환 - 페치 조인 최적화

✔️ OrderSimpleApiController - 추가

    /**
     * V3. 엔티티를 조회해서 DTO로 변환(fetch join 사용O)
     * - fetch join으로 쿼리 1번 호출
     * 참고: fetch join에 대한 자세한 내용은 JPA 기본편 참고(정말 중요함)
     */
    @GetMapping("/api/v3/simple-orders")
    public List<SimpleOrderDto> ordersV3() {
        List<Order> orders = orderRepository.findAllWithMemberDelivery();
        List<SimpleOrderDto> result = orders.stream()
                .map(o -> new SimpleOrderDto(o))
                .collect(toList());
        return result;
    }

✔️ OrderRepository - 추가 코드

    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();
    }
  • 엔티티를 페치 조인(fetch join)을 사용해서 쿼리 1번에 조회
  • 페치 조인으로 order ➡️ member , order ➡️ delivery 는 이미 조회 된 상태 이므로 지연로딩 X

➡️ 실행하면 V2를 실행했을 때와 같은 JSON 형식이 반환됨!

그러나 쿼리 형식이 다름!!
➡️ V2는 총 5번의 쿼리가 실행됐다
➡️ 그러나 V3는 단 1번의 쿼리만 실행됐다!!



🏷 간단한 주문 조회 V4 : JPA에서 DTO로 바로 조회

✔️ OrderSimpleApiController - 추가

    /**
     * V4. JPA에서 DTO로 바로 조회
     * - 쿼리1번 호출
     * - select 절에서 원하는 데이터만 선택해서 조회
     */
    @GetMapping("/api/v4/simple-orders")
    public List<OrderSimpleQueryDto> ordersV4() {
        return orderSimpleQueryRepository.findOrderDtos();
    }

✔️ OrderSimpleQueryRepository 조회 전용 리포지토리

../repository.order.simplequery.OrderSimpleQueryRepository.java 생성

package jpabook.jpashop.repository.order.simplequery;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import java.util.List;

@Repository
@RequiredArgsConstructor
public class OrderSimpleQueryRepository {

    private final EntityManager em;

    public List<OrderSimpleQueryDto> findOrderDtos() {
        return em.createQuery(
                "select new jpabook.jpashop.repository.order.simplequery.OrderSimpleQueryDto(o.id, m.name, o.orderDate, o.status, d.address)" +
                        " from Order o" +
                        " join o.member m" +
                        " join o.delivery d", OrderSimpleQueryDto.class)
                .getResultList();
    }
}

✔️ OrderSimpleQueryDto 리포지토리에서 DTO 직접 조회

../repository.order.simplequery.OrderSimpleQueryDto.java 생성

package jpabook.jpashop.repository.order.simplequery;

import jpabook.jpashop.domain.Address;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderStatus;
import lombok.Data;

import java.time.LocalDateTime;

public class OrderSimpleQueryDto {

    private Long orderId;
    private String name;
    private LocalDateTime orderDate;
    private OrderStatus orderStatus;
    private Address address;

    public OrderSimpleQueryDto(Long orderId, String name, LocalDateTime
            orderDate, OrderStatus orderStatus, Address address) {
        this.orderId = orderId;
        this.name = name;
        this.orderDate = orderDate;
        this.orderStatus = orderStatus;
        this.address = address;
    }

}
  • 일반적인 SQL을 사용할 때 처럼 원하는 값을 선택해서 조회
  • new 명령어를 사용해서 JPQL의 결과를 DTO로 즉시 변환
  • SELECT 절에서 원하는 데이터를 직접 선택하므로 DB 애플리케이션 네트워크 용량 최적화(생각보다 미비함)
  • 리포지토리 재사용성 떨어짐, API 스펙에 맞춘 코드가 리포지토리에 들어가는 단점이 있음



🏷 정리

쿼리 방식 선택 권장 순서

1️⃣ 우선 엔티티를 DTO로 변환하는 방법을 선택한다.
2️⃣ 필요하면 페치 조인으로 성능을 최적화 한다. 대부분의 성능 이슈가 해결된다🙂
3️⃣ 그래도 안되면 DTO로 직접 조회하는 방법을 사용한다.
4️⃣ 최후의 방법은 JPA가 제공하는 네이티브 SQL이나 스프링 JDBC Template을 사용해서 SQL을 직접 사용한다.


페치 조인의 세계란,,,,^_^

좋은 웹페이지 즐겨찾기