오봉이와 함께하는 개발 블로그

JPA 2 - 주문 조회V5 : JPA에서 DTO 직접 조회(컬렉션 조회 최적화) 본문

BE/JPA

JPA 2 - 주문 조회V5 : JPA에서 DTO 직접 조회(컬렉션 조회 최적화)

오봉봉이 2022. 9. 6. 21:39
728x90

주문 조회 V5: JPA에서 DTO 직접 조회 - 컬렉션 조회 최적화

OrderApiController에 추가

@GetMapping("/api/v5/orders")
public List<OrderQueryDto> ordersV5() {
    return orderQueryRepository.findAllByDto_optimization();
}

OrderQueryRepository에 추가

    /**
     * 최적화
     * Query: 루트 1번, 컬렉션 1번
     * 데이터를 한꺼번에 처리할 때 많이 사용하는 방식
     */
    public List<OrderQueryDto> findAllByDto_optimization() {
        //루트 조회(toOne 코드를 모두 한번에 조회)
        List<OrderQueryDto> result = findOrders();

        //orderItem 컬렉션을 MAP을 통해 한번에 조회(루프를 돌지 않고 in 쿼리 사용)
        List<Long> orderIds = toOrderIds(result);
        Map<Long, List<OrderItemQueryDto>> orderItemMap = findOrderItemMap(orderIds);

        //루프를 돌면서 컬렉션 추가(추가 쿼리 실행X)
        result.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId())));
        return result;

    }

    private List<Long> toOrderIds(List<OrderQueryDto> result) {
        return result.stream()
                .map(o -> o.getOrderId())
                .collect(Collectors.toList());
    }

    private Map<Long, List<OrderItemQueryDto>> findOrderItemMap(List<Long> orderIds) {
        List<OrderItemQueryDto> orderItems = em.createQuery(
                        "select new jpabook.jpashop.repository.order.query.OrderItemQueryDto" +
                                "(oi.order.id, i.name, oi.orderPrice, oi.count)" +
                                " from OrderItem oi" +
                                " join oi.item i" +
                                " where oi.order.id in :orderIds", OrderItemQueryDto.class)
                .setParameter("orderIds", orderIds)
                .getResultList();

        Map<Long, List<OrderItemQueryDto>> orderItemMap = orderItems.stream()
                .collect(Collectors.groupingBy(orderItemQueryDto -> orderItemQueryDto.getOrderId()));
        // orderItemQueryDto의 orderId를 기준으로 해서 List를 Map으로 바꿔줌.
        // key = orderItemQueryDto.getOrderId(), value = List<OrderItemQueryDto>

        return orderItemMap;

    }
}

정리

V4는 루프를 돌릴 때 마다 쿼리를 날렸는데 V5는 쿼리를 1번 날리고 메모리에 맵으로 가져온 후 메모리에서 매칭을 통해 값을 세팅해준다.
쿼리가 총 2번 나간다.

XToOne관계들을 먼저 조회하고, 여기서 얻은 식별자 orderIdXToMany관계인 OrderItemIN 쿼리를 통해 한꺼번에 조회한다.

  • Query
    • 루트 1번
    • 컬렉션 1번
  • MAP을 사용해서 매칭 성능 향상(O(1))
인프런 김영한 지식공유자님 강의 - 실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
728x90
Comments