참고 : [실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화] - 김영한
이번에는 컬렉션이 있는 주문 조회에서 DTO를 직접 조회하는 방법을 알아보자.
OrderApiController
...
private final OrderQueryRepository orderQueryRepository;
@GetMapping("/api/v4/orders")
public List<OrderQueryDto> ordersV4() {
return orderQueryRepository.findOrderQueryDtos();
}
...

OrderRepository는 Order 엔티티를 조회하는 용도로 쓰이며,
OrderQueryRepository는 화면이나 API처럼 의존관계가 있기 때문에 패키지를 나누어 주는 것이 좋다.
OrderQueryDto
@Data
public class OrderQueryDto {
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
private List<OrderItemQueryDto> orderItems;
public OrderQueryDto(Long orderId, String name, LocalDateTime orderDate, OrderStatus orderStatus, Address address) {
this.orderId = orderId;
this.name = name;
this.orderDate = orderDate;
this.orderStatus = orderStatus;
this.address = address;
}
}
OrderItemQueryDto
@Data
public class OrderItemQueryDto {
@JsonIgnore
private Long orderId;
private String itemName;
private int orderPrice;
private int count;
public OrderItemQueryDto(Long orderId, String itemName, int orderPrice, int count) {
this.orderId = orderId;
this.itemName = itemName;
this.orderPrice = orderPrice;
this.count = count;
}
}
OrderQueryRepository
@Repository
@RequiredArgsConstructor
public class OrderQueryRepository {
private final EntityManager em;
/**
* 컬렉션은 별도로 조회
* Query: 루트 1번, 컬렉션 N 번 * 단건 조회에서 많이 사용하는 방식
*/
public List<OrderQueryDto> findOrderQueryDtos(){
//루트 조회(toOne 코드를 모두 한번에 조회)
List<OrderQueryDto> result= findOrders();
//루프를 돌면서 컬렉션 추가(추가 쿼리 실행)
result.forEach(o->{
List<OrderItemQueryDto> orderItems=findOrderItems(o.getOrderId());
o.setOrderItems(orderItems);
});
return result;
}
/**
* 1:N 관계(컬렉션)를 제외한 나머지를 한번에 조회
*/
private List<OrderItemQueryDto> findOrderItems(Long orderId) {
return 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=:orderId",OrderItemQueryDto.class)
.setParameter("orderId",orderId)
.getResultList();
}
/**
* 1:N 관계인 orderItems 조회
*/
private List<OrderQueryDto> findOrders() {
return em.createQuery(
"select new jpabook.jpashop.repository.order.query.OrderQueryDto(o.id, m.name, o.orderDate," +
"o.status, d.address)" +
"from Order o " +
"join o.member m " +
"join o.delivery d", OrderQueryDto.class)
.getResultList();
}
}
findOrderItems() 같은 별도의 메서드로 조회한다.
- findOrders()는 한 번의 쿼리로 전체 주문을 조회
- findOrderItems()는 OrderQueryDto 리스트의 크기만큼 반복 실행되며, 각 주문의 OrderItem 리스트를 조회