ManyToOne, OneToOne 연관관계 조회 최적화 V2(Better)(DTO)

KMS·2022년 4월 19일
0

SpringBoot + JPA

목록 보기
10/14

Order -> Delivery를 OneToOne로 연관되어 있으며, Order -> Member는 ManyToOne으로 연관되어 있을때, Order를 통해 연관된 Delivery와 Member도 같이 DB로 부터 값을 가져와서 Response로 보내는 작업입니다.

V2: DTO로 값을 보내기

API

@GetMapping("/api/v2/simple-orders")
    public Result ordersV2() {
        List<Order> findOrders = orderRepository.findAllByCriteria(new OrderSearch()); //쿼리문 실행
        List<OrderDTO> orderDTOS = findOrders.stream()
                .map(o -> new OrderDTO(o))
                .collect(Collectors.toList());

        return new Result(orderDTOS);
}

DTO

	@Data
    static class OrderDTO {
        private Long orderId;
        private String username; //주문자 이름
        private LocalDateTime orderDate;
        private OrderStatus orderStatus;
        private Address address; //배송지

        public OrderDTO(Order order) {
            this.orderId = order.getId();
            this.username = order.getMember().getUsername();
            this.orderDate = order.getOrderDate();
            this.orderStatus = order.getStatus();
            this.address = order.getDelivery().getAddress();
        }
    }
	@Data
    @AllArgsConstructor
    static class Result<T>{
        public T data;
    }

(Result라는 DTO에 OrderDTO를 한 번 감싸서 보내는 이유는, 해당 API에서는 조회 값이 배열로 반환되는데, 배열을 그대로 반환하기 보다는 다른 DTO로 한번더 감싸서 보내는 것이 추천되기 때문입니다.)

V1과 동일한 방식으로 DB를 접근해서 값을 가져 왔지만, 해당 값들을 OrderDTO로 변환해서 보냄으로써 V1에서의 문제점을 해결 할 수 있었습니다. 또한, 받아온 값 중에서 스펙에 맞게 원하는 값만 Response로 보낼 수 있는 장점이 있습니다.
그러나, 모든 엔티티가 LAZY로 설정되어 있기 때문에 새로운 문제가 발생합니다. 바로 1+N 문제가 발생합니다.

1+N의 문제는 가져오는 값이 영속성 컨텍스트(1차 캐시)에 없어서 DB를 N번 만큼 더 조회하게 되는 문제점 입니다.
해당 예제에서는 findOrder를 가져올때 쿼리문을 한 번 실행해서 N개 결과를 가져옵니다. 이때, N개의 결과를 Order -> OrderDTO로 변환하는데, 각 결과 값과 연관된 Member와 Delivery는 지연 로딩(LAZY)이기 때문에 영속성 컨텍스트에 없습니다. 그렇기 때문에, DB를 또 접근해서 Member와 Delivery 값들을 가져와야 하고, 이 과정에서 2번의 쿼리문이 더 실행 됩니다.
그렇게 때문에, 최악의 경우에는 N개의 결과 값에 대해서 2번씩 쿼리문이 실행 되기 때문에 총 1+2N의 쿼리문이 발생 합니다.

/**
         * 결과:
         * (쿼리문:)
         * 1. select order0_.order_id as order_id1_9_, order0_.member_id as member_i4_9_, order0_.order_date as order_da2_9_, order0_.status as status3_9_ from orders order0_ inner join member member1_ on order0_.member_id=member1_.member_id where 1=1 limit ?
         * 2. select delivery0_.delivery_id as delivery1_4_0_, delivery0_.city as city2_4_0_, delivery0_.street as street3_4_0_, delivery0_.zipcode as zipcode4_4_0_, delivery0_.order_id as order_id6_4_0_, delivery0_.status as status5_4_0_ from delivery delivery0_ where delivery0_.order_id=?
         * 3. select delivery0_.delivery_id as delivery1_4_0_, delivery0_.city as city2_4_0_, delivery0_.street as street3_4_0_, delivery0_.zipcode as zipcode4_4_0_, delivery0_.order_id as order_id6_4_0_, delivery0_.status as status5_4_0_ from delivery delivery0_ where delivery0_.order_id=?
         * 4. select member0_.member_id as member_i1_6_0_, member0_.city as city2_6_0_, member0_.street as street3_6_0_, member0_.zipcode as zipcode4_6_0_, member0_.username as username5_6_0_ from member member0_ where member0_.member_id=?
         * 5. select member0_.member_id as member_i1_6_0_, member0_.city as city2_6_0_, member0_.street as street3_6_0_, member0_.zipcode as zipcode4_6_0_, member0_.username as username5_6_0_ from member member0_ where member0_.member_id=?
         * => 총 5번의 쿼리문이 처리됨
         *
         * Response:
         * {
         *     "data": [
         *         {
         *             "orderId": 4,
         *             "username": "userA",
         *             "orderDate": "2022-04-18T15:39:03.823905",
         *             "orderStatus": "ORDER",
         *             "address": {
         *                 "city": "seoul",
         *                 "street": "street",
         *                 "zipcode": "12345"
         *             }
         *         },
         *         {
         *             "orderId": 11,
         *             "username": "userB",
         *             "orderDate": "2022-04-18T15:39:03.906687",
         *             "orderStatus": "ORDER",
         *             "address": {
         *                 "city": "ulsan",
         *                 "street": "blvd",
         *                 "zipcode": "22345"
         *             }
         *         }
         *     ]
         * }
         */
profile
Student at Sejong University Department of Software

0개의 댓글