V3는 엔티티를 DTO 로 변환하는 과정이 있었습니다.
근데 이렇게 하지 않고 바로 DTO 에 데이터를 담게 되면 좀 더 성능 최적화를 할 수 있습니다.
@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {
private final OrderRepository orderRepository;
@GetMapping("/api/v4/simple-orders")
public List<OrderSimpleQueryDto> ordersV4() {
return orderRepository.findOrderDtos();
}
}
package jpabook.jpashop.repository;
@Data
public class OrderSimpleQueryDto { //...1
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
public OrderSimpleQueryDto(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;
}
}
기존에는 간단하게 한다고 그냥 컨트롤러에 DTO 를 내부 클래스로 만들었습니다.
근데 지금 상황에서 컨트롤러에 DTO 를 그냥 만들면 좀 리포지토리가 컨트롤러를 의존하는 이상한 상황이 벌어집니다. 왜냐면 리포지토리에서 바로 DTO 에 데이터를 받으려고 하기 때문이죠.
그래서 따로 class 를 만들었습니다.
의존관계는 핵사고나 아키텍처 처럼 인터페이스를 발라내는게 아니면 가급적 한 방향으로만 흘러야합니다. 컨트롤러에서 서비스, 서비스에서 리포지토리로 가는 것 처럼요.
@Repository
@RequiredArgsConstructor
public class OrderRepository {
private final EntityManager em;
public List<OrderSimpleQueryDto> findOrderDtos() {
return em.createQuery(
"select new jpabook.jpashop.repository.OrderSimpleQueryDto(o.id, m.name, o.orderDate, o.status, d.address)" + //...1
" from Order o" +
" join o.member m" +
" join o.delivery d", OrderSimpleQueryDto.class)
.getResultList();
}
}
JPA 는 기본적으로 Entity 나 @Embeddable 를 붙인 임베디드 타입인 VO (value object)만 반환할 수 있습니다.
DTO 에 맵핑을 하기 위해서는 먼저 new 명령어를 클래스 경로와 같이 포함해서 써야합니다.
그리고 일반적인 SQL 처럼 원하는 값을 선택해서 조회해야 합니다.
참고로 d.address 는 VO 라서 그대로 써도 되죠.
실행을 하면 V3 와 결과는 똑같습니다.
그런데 내부적인 쿼리를 보면 살짝 다릅니다.
from 이나 join 은 동일하지만 select 는 훤하는 것들만 조회하고 있습니다.