# 주문 조회 V6: JPA에서 DTO 직접 조회, 플랫 데이터 최적화

Order와 OrderItem, Item을 join해서 한번에 다 가져오도록 해본다!\
SQL에서는 flat하게 한줄로 나오기 때문에 이에 맞춰야한다.

OrderQueryDto로 바꾸고 싶다면 직접 중복을 제거해야한다.(a.k.a 노가다)

```java
@GetMapping("/api/v6/orders")
public List<OrderFlatDto> ordersV6(){
    return orderQueryRepository.findAllByDto_flat();
}
```

작전 : 한번에 다 join해서 쿼리가 한번만 실행되게 한다.\
\=>당연히 중복 결과가 나온다!\
API Specification은 이전과 동일하게 해서 중복되는 데이터들은 직접 복잡한 쿼리들을 작성해서 제거한다.

그러면 한번의 쿼리로 원하는 데이터를 조회할 수 있다!

```java
@GetMapping("/api/v6/orders")
  public List<OrderQueryDto> ordersV6() {
      List<OrderFlatDto> flats = orderQueryRepository.findAllByDto_flat();
      return flats.stream()
              .collect(groupingBy(o -> new OrderQueryDto(o.getOrderId(),
  o.getName(), o.getOrderDate(), o.getOrderStatus(), o.getAddress()),
                      mapping(o -> new OrderItemQueryDto(o.getOrderId(),
  o.getItemName(), o.getOrderPrice(), o.getCount()), toList())
              )).entrySet().stream()
              .map(e -> new OrderQueryDto(e.getKey().getOrderId(),
  e.getKey().getName(), e.getKey().getOrderDate(), e.getKey().getOrderStatus(),
  e.getKey().getAddress(), e.getValue()))
              .collect(toList());
}
```

**OrderFlatDto**\
OrderDTO와 OrderItemDTO를 합친다!

```java
package jpabook_v2.jpashop_v2.repository.order.query;

import jpabook_v2.jpashop_v2.domain.Address;
import jpabook_v2.jpashop_v2.domain.OrderStatus;
import lombok.Data;

import java.time.LocalDateTime;

@Data
public class OrderFlatDto {
    private Long orderId;
    private String name;
    private LocalDateTime orderDate;
    private OrderStatus orderStatus;
    private Address address;

    private String itemName;
    private int orderPrice;
    private int count;

    public OrderFlatDto(Long orderId, String name, LocalDateTime orderDate, OrderStatus orderStatus, Address address, String itemName, int orderPrice, int count) {
        this.orderId = orderId;
        this.name = name;
        this.orderDate = orderDate;
        this.orderStatus = orderStatus;
        this.address = address;
        this.itemName = itemName;
        this.orderPrice = orderPrice;
        this.count = count;
    }
}
```

**OrderQueryRepository : 쿼리 작성**

```java
public List<OrderFlatDto> findAllByDto_flat() {
    return  em.createQuery(
            "select new jpabook_v2.jpashop_v2.repository.order.query.OrderFlatDto(o.id, m.name, o.orderDate, o.status, d.address, i.name, oi.orderPrice, oi.count)" +
                    " from Order o" +
                    " join o.member m" +
                    " join o.delivery d" +
                    " join o.orderItems oi" +
                    " join oi.item i", OrderFlatDto.class)
            .getResultList();
}
```

장점 : **쿼리 한번**

단점

* 쿼리는 한번이지만 조인으로 인해 DB에서 애플리케이션에 전달하는 데이터에 중복 데이터가 추가되므로 상황에 따라 V5 보다 더 느릴 수 도 있음!
* 애플리케이션에서 추가 작업이 크다.
* **페이징 불가능**\
  OrderFlatDto 나 OrderItems 관련 : 페이징 가능할 수도 있지만 Order에 대해서는 페이징 불가능.\
  \=>DB에서는 여전히 중복된 데이터가 포함된 테이블이 있기 때문.\
  ex) DB : 4, 4, 11, 11 주문번호에 따라 이렇게 저장되있는데\
  우리가 원하는 것은 4,11 이다. 그래서 페이징으로 (0,2) 을 하면 4,4가 나와버린다.
