X to One 방식과 마찬가지로 fetch join을 사용하여 LAZY 로딩 없이 하나의 쿼리로 전부 가져올 수 있다.

 

 

    @GetMapping("/api/v3/orders")
    public List<OrderDto> ordersV3() {
        List<Order> orders = orderRepository.findAllWithItem();

        // order를 dto로 변환하여 반환
        List<OrderDto> result = orders.stream()
                .map(o -> new OrderDto(o))
                .collect(Collectors.toList());
        return result;
    }
    
    @Data
    static class OrderDto {

        private Long orderId;
        private String name;
        private LocalDateTime orderDate;
        private OrderStatus orderStatus;
        private Address address;
        private List<OrderItemDto> orderItems;
        // DTO 안에 엔티티가 있는 것도 잘못된 설계
        // 이조차도 이런식으로 전부 DTO로 바꿔줘야 함

        public OrderDto(Order order) {
            orderId = order.getId();
            name = order.getMember().getName();
            orderDate = order.getOrderDate();
            orderStatus = order.getStatus();
            address = order.getDelivery().getAddress();
            orderItems = order.getOrderItems().stream()
                    .map(orderItem -> new OrderItemDto(orderItem))
                    .collect(Collectors.toList());
        }
    }

    @Data
    static class OrderItemDto {

        private String itemName;  // 상품명
        private int orderPrice;  // 주문 가격
        private int count;  // 주문 수량

        public OrderItemDto(OrderItem orderItem) {
            itemName = orderItem.getItem().getName();
            orderPrice = orderItem.getOrderPrice();
            count = orderItem.getCount();
        }
    }
    
    .
    .
    .
    
    // OrderRepository 내부
    public List<Order> findAllWithItem() {
        return  em.createQuery(
                // JPQL distinct 기능 두 가지
                // 1. DB에 날리는 쿼리 수준에서 distinct 수행
                // 2. 그래도 걸러지지 않은 것들에 대해서는 o의 참조값을 확인하여 중복 제거
                "select distinct o from Order o" +
                        " join fetch o.member m" +
                        " join fetch o.delivery d" +
                        " join fetch o.orderItems oi" +
                        " join fetch oi.item i", Order.class)
                .getResultList();
    }
    
    
    

 

fetch join을 사용하지 않으면 order, orderItem, item 각각의 개수마다 쿼리가 1개씩 나가게 된다.

 

order를 조회하는 JPQL에서 연관된 모든 엔티티에 fetch join을 걸어주면 한 번의 쿼리만으로 모든 엔티티를 동시에 가져올 수 있다.

 

이때 distinct의 역할이 중요하다.

 

1:N 관계에서 join 쿼리를 수행할경우 row의 개수는 N쪽(여기선 orderItems)으로 맞춰진다. 즉, 1쪽(여기선 order)의 데이터가 중복해서 들어가게 된다.

다시말해서 똑같은 o가 여러 개가 나오는 것.

 

이러한 중복을 막기 위해 distinct를 사욯안다.

 

distinct는 두 가지 기능을 수행한다.

1. 일반적인 쿼리문의 distinct과 똑같은 기능

-> 하지만 위의 경우에서는 order만 같을 뿐 item 등의 정보는 모두 다르기 때문에 distinct에서 걸러지지 않는다 ( distinct는 row의 모든 column이 같을 때만 중복으로 판단 )

 

2. 똑같은 참조값을 가진 객체 중복 제거

-> 여기서 o의 중복이 제거된다.

 

 

 

 

이러한 방식은 성능을 줄여주는 데 큰 도움이 되지만 페이징 기능을 사용할 수 없다는 단점이 있다.

(원인에 대해서는 교재or강의를 참고하자 V3.1 강의의 16:30 ~ 23:30)

 

 

결론 : X To One 관계는 페치 조인을 해도 페이징에 아무런 영향이 없다. 따라서 X To One 관계는 페치 조인으로 한 번에 가져와서 쿼리 수를 줄이고, X To Many 관계는 hibernate: default_batch_fetch_size로 해결하자.

 

 

 

 

+ Recent posts