public List<Order> findAllWithMemberDelivery() {
return em.createQuery(
"select o from Order o " +
" join fetch o.member m " +
" join fetch o.delivery d ", Order.class
).getResultList();
}
V3
@GetMapping("/api/v3/simple-orders")
public List<SimpleOrderDto> orderV3(){
List<Order> orders = orderRepository.findAllWithMemberDelivery();
List<SimpleOrderDto> result = orders.stream()
.map(o -> new SimpleOrderDto(o))
.collect(Collectors.toList());
return result;
}
@Data
static class SimpleOrderDto{
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
public SimpleOrderDto(Order order){
orderId = order.getId();
name =order.getMember().getName();
orderDate = order.getOrderDate();
orderStatus = order.getStatus();
address = order.getDelivery().getAddress();
}
}
DB에 날린 쿼리
V2는 for문을 돌 때 쿼리가 추가로 DB에 날렸는데, V3에서는 join fetch를 통해 쿼리가 1개만 나간 것을 볼 수 있음.
결과
간단한 주문 조회: JPA에서 DTO로 바로 조회
재사용성이 낮다는 단점이 있음.
성능 최적화가 V3보다 좀 더 좋음
API 스펙이 바뀌면 repository를 고쳐야 한다는 단점도 존재.
패키지를 하나 더 만들어서 Dto랑 Query만 따로 뽑아서 사용하는 것도 방법 중 하나
예) repository/order/query 안에다가 Dto와 쿼리를 따로 저장.
V4
@GetMapping("/api/v4/simple-orders")
public List<OrderSimpleQueryDto> orderV4(){
return orderRepository.findOrderDtos();
}
public List<OrderSimpleQueryDto> findOrderDtos() {
return em.createQuery(
// new 경로.Dto 꼭 적어주어야함
"select new jpabook.jpashop.repository.OrderSimpleQueryDto(o.id, m.name, o.orderDate, o.status, d.address)" +
" from Order o " +
" join o.member m " +
" join o.delivery d", OrderSimpleQueryDto.class).getResultList();
}
쿼리
필요한 데이터만 뽑아서 Dto로 변환(V3는 모든 데이터를 가져옴)
정리
엔티티를 DTO로 변환하거나, DTO로 바로 조회하는 두가지ㅣ 방법은 각각 장단점이 있다. 둘중 상황에 따라서 더나은 방법을 선택하면 됨. 엔티티를 조회하면 리포지토리 재사용성도 좋고 개발도 단순. 따라서 권장하는 방법은 다음과 같음
우선 엔티티를 DTO로 변환하는 방법을 선택
필요하면 페치 조인으로 성능을 최적화 -> 대부분의 성능 이슈가 해결
그래도 안되면 DTO로 직접 조회화는 방법을 사용
최후의 방법은 JPA가 제공하는 네이티브 SQL이나 스프링 JDBC Template을 사용해서 SQL을 직접 사용