주문 조회 V5: JPA에서 DTO 직접 조회 - 컬렉션 조회 최적화
이전 버전과 차이점(핵심)
Before : foreach 돌 때마다 쿼리문 실행 After : in 절로 메모리에 데이터 한번에 가져온 후, foreach 돌 때마다 메모리에서 값 매칭되는 데이터 셋팅 =>쿼리 2번으로 최적화 가능!
OrderApiController
OrderQueryRepository findAllByDto_optimization(메인) findOrderItemMap//리팩토링 결과 생성된 메서드
toOrderIds//리팩토링 결과 생성된 메서드
OrderApiController
@GetMapping("/api/v5/orders")
public List<OrderQueryDto> ordersV5(){
return orderQueryRepository.findAllByDto_optimization();
}
OrderQueryRepository
findAllByDto_optimization(메인) 일단 findOrders()로 Member & Delivery 조인해서 한번에 Order 조회하는 것은 동일하다.
public List<OrderQueryDto> findAllByDto_optimization() {
List<OrderQueryDto> result = findOrders();
Map<Long, List<OrderItemQueryDto>> orderItemMap = findOrderItemMap(toOrderIds(result));
result.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId())));
return result;
}
2. findOrderItemMap(List<Long> orderIds) OrderItem 조회쿼리 앞과 비슷하지만 여기서 핵심은 in절로 orderId 하나씩 조회하는 게 아니라 한번에 다 가져온다?! 그래서 orderIds 를 orderItems에 저장한다.
orderItems의 id, orderItemDTO를 Map으로 저장=> key로 조회 시 O(1)으로 성능 최적화&코드 간결❤️
private Map<Long, List<OrderItemQueryDto>> findOrderItemMap(List<Long> orderIds) {
List<OrderItemQueryDto> orderItems = em.createQuery("select new jpabook_v2.jpashop_v2.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)//findOrders()로 가져온 List<OrderQueryDto>를 stream, map으로 돌려서 id를 다 뽑는다!
.getResultList();
Map<Long, List<OrderItemQueryDto>> orderItemMap = orderItems.stream()
.collect(Collectors.groupingBy(orderItemQueryDto -> orderItemQueryDto.getOrderId()));
return orderItemMap;
}
3. toOrderIds(List<OrderQueryDto> result) findOrders()로 조회한 List<OrderQueryDto>를 foreach로 각각의 Id들을 리스트로 만들어서 반환
private List<Long> toOrderIds(List<OrderQueryDto> result) {
List<Long> orderIds = result.stream()
.map(o -> o.getOrderId())
.collect(Collectors.toList());
return orderIds;
}
fetch join과 트레이드 오프
- : JPA에서 DTO 직접 조회 시, 상당히 많은 쿼리들을 직접 작성 + : select쿼리문이 fetch join에 비해 상당히 적다!!
결론, 정리
쿼리 : root 1번, 컬렉션 1번(총 2번)
ToOne 관계들 먼저 join으로 조회하고, 여기서 얻은 식별자 orderId로 ToMany관계인 orderItem을 한번에 조회
MAP을 사용해서 매칭 성능 향상(O(1))
Last updated
Was this helpful?