지연 로딩과 조회 성능 최적화 - 엔티티 DTO로 변환, fetch join

HotFried·2023년 11월 23일
0

DTO 변환 코드

@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {

    private final OrderRepository orderRepository;

    @GetMapping("/api/v2/simple-orders")
    public List<SimpleOrderDto> ordersV2() {
        return orderRepository.findAllByString(new OrderSearch()).stream()
                .map(SimpleOrderDto::new)
                .collect(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 Loading
            orderDate = order.getOrderDate();
            orderStatus = order.getStatus();
            address = order.getDelivery().getAddress(); // Lazy Loading
        }
    }
}

order.getMember()order.getDelivery()에서 연관 관계에 있는 데이터를 지연 로딩으로 불러온다.


결과

  • 쿼리가 총 1 + N + N번 실행된다. (v1과 쿼리수 결과는 같다.)
    Order 검색1번, Lazy Loading 필드 N번
    • order 조회 1번(order 조회 결과 수가 N이 된다.)
    • order -> member 지연 로딩 조회 N 번
    • order -> delivery 지연 로딩 조회 N 번

ex) order의 결과가 4개면 최악의 경우 1 + 4 + 4번 실행된다.(최악의 경우)
지연로딩은 영속성 컨텍스트에서 조회하므로, 이미 조회된 경우 쿼리를 생략한다


Fetch join

참고 : 객체지향 쿼리 언어 - 중급 문법 (fetch join) [매우 중요]

@GetMapping("/api/v3/simple-orders")
    public List<SimpleOrderDto> ordersV3() {
        return orderRepository.findAllWithMemberDelivery().stream()
                .map(SimpleOrderDto::new)
                .collect(toList());
    }
public class 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
    (프록시 대신 실제 값으로 다 채워서 가져온다.)

참고 :

실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화

profile
꾸준하게

0개의 댓글