ManyToOne最適化OneToOne関連クエリーV 3とV 4(最適)


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

V3: JOIN FETCH + DTO


API

	@GetMapping("/api/v3/simple-orders")
    public Result ordersV3() {
        List<Order> findOrders = orderRepository.findALlWithOrderMember();
        List<OrderDTO> orderDTOS = findOrders.stream()
                .map(o -> new OrderDTO(o))
                .collect(Collectors.toList());

        return new Result(orderDTOS);
}

findAllWithOrderMember()

public List<Order> findALlWithOrderMember() {
        String query = "select o from Order o" +
                        " join fetch o.member m" +
                        " join fetch o.delivery d";

        return em.createQuery(query, Order.class)
                .getResultList();
    }
送信応答はV 2と同じDTOを返し,戻り値も同じである.ただし、findOrdersをインポートする場合も、JOIN FETCHをクエリー文として使用して関連関係のエンティティ値をインポートします.したがって、Order->OrderDTO変換では、DBにアクセスすることなく、メンバーと成果物の値をインポートできます.
実際に処理されたクエリー文をチェックすることで、find Ordersをインポートした場合にのみクエリー文が処理されると判断できます.
		/**
         * 결과:
         * (쿼리문:)
         * 1. select order0_.order_id as order_id1_9_0_, member1_.member_id as member_i1_6_1_, delivery2_.delivery_id as delivery1_4_2_, order0_.member_id as member_i4_9_0_, order0_.order_date as order_da2_9_0_, order0_.status as status3_9_0_, member1_.city as city2_6_1_, member1_.street as street3_6_1_, member1_.zipcode as zipcode4_6_1_, member1_.username as username5_6_1_, delivery2_.city as city2_4_2_, delivery2_.street as street3_4_2_, delivery2_.zipcode as zipcode4_4_2_, delivery2_.order_id as order_id6_4_2_, delivery2_.status as status5_4_2_ from orders order0_
         * inner join member member1_ on order0_.member_id=member1_.member_id
         * inner join delivery delivery2_ on order0_.order_id=delivery2_.order_id
         * => 총 1번의 쿼리문이 처리됨
         */
しかし,不要なColumnをすべて導入したため,これらの点では最適化の余地があり,V 4でこれについて議論する.

V4: JOIN(DTO) + DTO


応答として値を送信するとDTOも返されますが、DBにアクセスするとDTOを使用して必要な値も取得されます.(詳細はhttps://velog.io/@k_ms1998/JPA-JPQL-%ED%94%84%EB%A1%9C%EC%A0%9D%EC%85%98を参照)
DBアクセス時にJOIN FETCHとDTOを併用することはできないので、JOIN(INNER、OUTER)とDTOを使用します.

API

	@GetMapping("/api/v4/simple-orders")
    public Result ordersV4() {
        List<OrderQueryDTO> orderDTOS = orderRepository.findALlWithOrderMemberDTO();

        return new Result(orderDTOS);
        /**
         * 결과:
         * (쿼리문:)
         * 1. select order0_.order_id as col_0_0_, member1_.username as col_1_0_, order0_.order_date as col_2_0_, order0_.status as col_3_0_, delivery2_.city as col_4_0_, delivery2_.street as col_4_1_, delivery2_.zipcode as col_4_2_ from orders order0_
         * inner join member member1_ on order0_.member_id=member1_.member_id
         * inner join delivery delivery2_ on order0_.order_id=delivery2_.order_id
         * => 총 1번의 쿼리문이 처리됨 & V3이랑 비교 해봤을때 가져오는 Column이 다름
         *
         * 반환되는 Response는 V2&V3와 동일
         */
    }

findALlWithOrderMemberDTO()

public List<OrderQueryDTO> findALlWithOrderMemberDTO() {
        String query = "select new jpabook.jpashop.Repository.DTO.OrderQueryDTO(o.id, m.username, o.orderDate, o.status, d.address) from Order o" +
                " join o.member m" +
                " join o.delivery d";

        return em.createQuery(query, OrderQueryDTO.class)
                .getResultList();
    }

OrderQueryDTO

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

    public OrderQueryDTO(Long orderId, String username, LocalDateTime orderDate, OrderStatus orderStatus, Address address) {
        this.orderId = orderId;
        this.username = username;
        this.orderDate = orderDate;
        this.orderStatus = orderStatus;
        this.address = address;
    }
}
応答値は同じで、クエリー文も一度入力するだけで必要な値を得ることができ、実行されたクエリーを表示すると、インポートされたColumnがV 3と異なることがわかります.

V3 vs. V4


V 3とV 4のどちらが無条件に良いかは言い難い.もちろん、V 4はさらに最適化されていますが、クエリーの最適化時にColumnを追加してもパフォーマンスに大きな影響はありません.また、V 4は1つの仕様でのみ使用可能なので、繰り返し使用できません.逆に、V 3は、複数の仕様で繰り返し使用できるという利点がある.
そのため、状況に応じてV 3かV 4を使うべきだと思います.