ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링부트/웹 애플리케이션 개발]API 개발 고급 - 컬렉션 조회 최적화 -1,2
    스프링&스프링부트 2023. 1. 13. 14:03

    - 컬렉션인 일대다 관계 (OneToMany)를 조회하고, 최적화하는 방법

    - 주문내역에서 추가로 주문한 상품 정보를 추가로 조회

    - Order 기준으로 컬렉션인 OrderItem 와 Item 이 필요하다.

     

     

     

    주문조회 V1: 엔티티 직접 노출

    package jpabook.jpashop.api;
    
    import jpabook.jpashop.domain.Order;
    import jpabook.jpashop.domain.OrderItem;
    import jpabook.jpashop.repository.OrderRepository;
    import jpabook.jpashop.repository.OrderSearch;
    import lombok.RequiredArgsConstructor;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    
    @RestController
    @RequiredArgsConstructor
    public class OrderApiController {
    
        private final OrderRepository orderRepository;
    
        @GetMapping("/api/v1/orders")
        public List<Order> ordersV1() {
            List<Order> all = orderRepository.findAllByString(new OrderSearch());
            for (Order order : all) {
                order.getMember().getName(); //Lazy 강제 초기화
                order.getDelivery().getAddress(); //Lazy 강제 초기환
                List<OrderItem> orderItems = order.getOrderItems();
    //            for (OrderItem orderItem : orderItems) {
    //                orderItem.getItem().getName();
    //            }
                orderItems.stream().forEach(o -> o.getItem().getName());
            }
            return all;
        }
    
    }

    - orderItem , item 관계를 직접 초기화하면 Hibernate5Module 설정에 의해 엔티티를 JSON으로 생성한다

    - 양방향 연관관계면 무한 루프에 걸리지 않게 한곳에 @JsonIgnore 를 추가해야 한다

    - 엔티티를 직접 노출하므로 좋은 방법은 아니다

     

     

     

    주문조회 V2: 엔티티를 DTO로 변환

    @GetMapping("/api/v2/orders")
    public List<OrderDto> ordersV2() {
        List<Order> orders = orderRepository.findAllByString(new OrderSearch());
        List<OrderDto> collect = orders.stream()
                .map(o -> new OrderDto(o))
                .collect(Collectors.toList());
        return collect;
    }
    
    @Data
    static class OrderDto {
        private Long orderId;
        private String name;
        private LocalDateTime orderDate;
        private OrderStatus orderStatus;
        private Address address;
        private List<OrderItem> orderItems;
    
        public OrderDto(Order order) {
            orderId = order.getId();
            name = order.getMember().getName();
            orderDate = order.getOrderDate();
            orderStatus = order.getStatus();
            address = order.getDelivery().getAddress();
            orderItems = order.getOrderItems();
        }
    
    }

    - orderItems > 엔티티이기 때문에 null

        > OrderDto  코드 추가

    public OrderDto(Order order) {
        orderId = order.getId();
        name = order.getMember().getName();
        orderDate = order.getOrderDate();
        orderStatus = order.getStatus();
        address = order.getDelivery().getAddress();
        order.getOrderItems().stream().forEach(o -> o.getItem().getName()); //프록시 초기화
        orderItems = order.getOrderItems();
    }

    - dto로 반환했을 때 dto 안에 엔티티가 있으면 안 된다

        > 엔티티가 외부에 노출 됨

        > 엔티티에 대한 의존을 완전히 끊어야한다

     

    @Data
    static class OrderDto {
        private Long orderId;
        private String name;
        private LocalDateTime orderDate;
        private OrderStatus orderStatus;
        private Address address;
        //private List<OrderItem> orderItems;
        private List<OrderItemDto> orderItems;
    
        public OrderDto(Order order) {
            orderId = order.getId();
            name = order.getMember().getName();
            orderDate = order.getOrderDate();
            orderStatus = order.getStatus();
            address = order.getDelivery().getAddress();
            order.getOrderItems().stream().forEach(o -> o.getItem().getName()); //프록시 초기화
            orderItems = order.getOrderItems().stream()
                    .map(orderItem -> new OrderItemDto(orderItem))
                    .collect(Collectors.toList());
        }
    
    }
    
    @Getter
    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();
        }
    }

    - 지연로딩 많음 > 실행되는 sql의 양이 많음

    - 지연 로딩은 영속성 컨텍스트에 있으면 영속성 컨텍스트에 있는 엔티티를 사용하고 없으면 SQL을 실행한다. 따라서 같은 영속성 컨텍스트에서 이미 로딩한 회원 엔티티를 추가로 조회하면 SQL을 실행하지 않는다. 

    728x90
Designed by Tistory.