V3는 엔티티를 DTO 로 변환하는 과정이 있었습니다.

근데 이렇게 하지 않고 바로 DTO 에 데이터를 담게 되면 좀 더 성능 최적화를 할 수 있습니다.

간단한 주문 조회 V4: JPA에서 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;
    }

}
  1. 기존에는 간단하게 한다고 그냥 컨트롤러에 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();
    }

}

  1. JPA 는 기본적으로 Entity 나 @Embeddable 를 붙인 임베디드 타입인 VO (value object)만 반환할 수 있습니다.

    DTO 에 맵핑을 하기 위해서는 먼저 new 명령어를 클래스 경로와 같이 포함해서 써야합니다.

    그리고 일반적인 SQL 처럼 원하는 값을 선택해서 조회해야 합니다.

    참고로 d.address 는 VO 라서 그대로 써도 되죠.

실행을 하면 V3 와 결과는 똑같습니다.

Untitled

그런데 내부적인 쿼리를 보면 살짝 다릅니다.

Untitled

from 이나 join 은 동일하지만 select 는 훤하는 것들만 조회하고 있습니다.