ManyToOne,OneToOne関連クエリー最適化V 2(DTO)


Order->デリバリはOneToOneに関連付けられ、Order->メンバーはManyToOneに関連付けられている場合、Orderに関連付けられたデリバリとメンバーもデータベースから値を取得し、応答として送信する操作です.

V 2: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;
    }
(Order DTOパッケージをResultというDTOで一度送信するのは、APIが直接配列を返すのではなく配列にクエリー値を返すため、クエリー値を他のDTOに再パッケージして送信することをお勧めします.)
V 1と同様にデータベースにアクセスして値を取得するが、これらの値をOrderDTO送信に変換することで、V 1の問題を解決する.また、仕様に合致する値のみを応答として送信できるという利点があります.
ただし、すべてのエンティティがLAZYに設定されているため、新しい問題が発生します.すぐに1+Nの問題が発生.
1+Nの問題は、インポートされた値が永続コンテキスト(プライマリキャッシュ)に含まれていないため、データベースのクエリー回数がN回に増えることです.
この例ではfindOrderをインポートすると、クエリ文が1回実行され、N個の結果が得られます.このとき、N個の結果をOrder->OrderDTOに変換します.各結果値に関連付けられたメンバーおよびデリバリは、遅延ロード(LAZY)であるため、永続性コンテキストにありません.したがって、メンバーとデリバリ値を取得するためにデータベースに再アクセスし、このプロセスでクエリー文を2回実行する必要があります.
したがって、最悪の場合、N個の結果値に対して2回のクエリが実行され、合計1+2 N個のクエリ文が生成される.
/**
         * 결과:
         * (쿼리문:)
         * 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"
         *             }
         *         }
         *     ]
         * }
         */