> For the complete documentation index, see [llms.txt](https://heunnajo.gitbook.io/jpa-2-api/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://heunnajo.gitbook.io/jpa-2-api/4.-api/v4-jpa-dto-1.md).

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

**이전 버전과 차이점(핵심)**

Before : foreach 돌 때마다 쿼리문 실행\
After : **in 절로** 메모리에 데이터 한번에 가져온 후, foreach 돌 때마다 **메모리에서** 값 매칭되는 데이터 셋팅\
\=>쿼리 2번으로 최적화 가능!

1. OrderApiController
2. OrderQueryRepository\
   **findAllByDto\_optimization(메인)**\
   findOrderItemMap//리팩토링 결과 생성된 메서드

   toOrderIds//리팩토링 결과 생성된 메서드

OrderApiController

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

OrderQueryRepository

1. **findAllByDto\_optimization(메인)**\
   **일단 findOrders()로 Member & Delivery 조인해서 한번에 Order 조회하는 것은 동일하다.**

```java
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;
}
```

&#x20; 2\. findOrderItemMap(List\<Long> orderIds)\
&#x20;   OrderItem 조회쿼리 앞과 비슷하지만 여기서 핵심은 **in절**로 orderId 하나씩 조회하는 게 아니라 한번에 다 가져온다?! 그래서 orderId**s 를 orderItems에 저장한다.**

orderItems의 id, orderItemDTO를 Map으로 저장=> **key로 조회 시 O(1)으로 성능 최적화&코드 간결❤️**

```java
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;
}
```

&#x20; 3\. toOrderIds(List\<OrderQueryDto> result)\
&#x20; **findOrders()로 조회한 List\<OrderQueryDto>를 foreach로 각각의 Id들을 리스트로 만들어서 반환**

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

**fetch join과 트레이드 오프**

&#x20;\- : JPA에서 DTO 직접 조회 시, 상당히 많은 쿼리들을 직접 작성\
\+ : select쿼리문이 fetch join에 비해 상당히 적다!!

결론, 정리

* 쿼리 : root 1번, 컬렉션 1번(총 2번)
* ToOne 관계들 먼저 join으로 조회하고, **여기서 얻은 식별자 orderId로 ToMany관계인 orderItem을 한번에 조회**
* MAP을 사용해서 매칭 성능 향상(O(1))
