실전! 스프링 부트와 JPA 활용2(API 개발과 성능 최적화)

Ch04. API 개발 고급(컬렉션 조회 최적화) - 주문 조회 V5(JPA에서 DTO 직접 조회 (컬렉션 조회 최적화))

webmaster 2021. 12. 21. 16:13
728x90

Controller

OrderQueryRepositoryV5 실행

OrderRepositoryV5

public List<OrderQueryDto> findAllByDtoOptimization() {
    List<OrderQueryDto> result = findOrders();
    //이전꺼는 루프를 도는 단점이 있었음

    Collection<Long> orderIds = toOrderIds(result);

    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 = findOrderItemMap(orderItems);
    result.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId())));
    //쿼리가 총 2번 나간다.

    return result;
}

private Map<Long, List<OrderItemQueryDto>> findOrderItemMap(List<OrderItemQueryDto> orderItems) {
    return orderItems.stream()
            .collect(Collectors.groupingBy(orderItemQueryDto -> orderItemQueryDto.getOrderId()));
}

private List<Long> toOrderIds(List<OrderQueryDto> result) {
    return result.stream()
            .map(o -> o.getOrderId())
            .collect(Collectors.toList());
}
  • Query: 루트 1번, 컬렉션 1번
  • ToOne 관계들을 먼저 조회하고, 여기서 얻은 식별자 orderId로 ToMany 관계인 OrderItem을 한꺼번에 조회
  • MAP을 사용해서 매칭 성능 향상(O(1))
  • In 쿼리를 사용하여 한번에 여러 개의 데이터를 조회한다.
  • Stream 객체를 활용하여 Map에 담아 result에 List로 반환해서 출력한다.
  • 쿼리 총 2번만 실행된다.
728x90