JPA에서 DTO로 바로 조회

학습 페이지

이전에는 엔티티를 통해서 조회한 후에, 이걸 DTO로 변형하는 과정을 거쳤었다. 이제는 그렇게 하지말고, JPA가 DTO를 바로 조회할 수 있도록 최적화 해보자.

이를 위해 리포지에 다음의 메서드를 추가한다.

        public List<OrderSimpleApiController.SimpleOrderDto> findOrderDtos(){
        
        }

지금 문제는… 리포지가 컨트롤러를 의존하고 있다는 것임. 왜? 위의 DTO는 지금까지 연습을 위해 컨트롤러단에 임시로 이너클래스로 만들었기 때문이다. 이런일은 절대 있어선 안된다. 의존관계는 한방향으로만 흘러야 한다. 차라리 DTO를 따로 빼주자.

package jpabook.jpashop.repository;

import jpabook.jpashop.domain.Address;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderStatus;
import lombok.Data;

import java.time.LocalDateTime;

@Data
public class OrderSimpleQueryDto {

      private Long orderId;
      private String name;
      private LocalDateTime orderDate;
      private OrderStatus orderStatus;
      private Address address;

      public OrderSimpleQueryDto(Order order) {
          orderId = order.getId();
          name = order.getMember().getName();
          orderDate = order.getOrderDate();
          orderStatus = order.getStatus();
          address = order.getDelivery().getAddress();
        }
    }

그리고 리포지 메서드를 마저 완성하자.

        public List<OrderSimpleQueryDto> findOrderDtos(){
           return em.createQuery(
                    "select o from Order o" +
                            " join o.member m" +
                            " join o.delivery d", OrderSimpleQueryDto.class) //반환타입이 DTO
                    .getResultList();
        

위 코드를 보면 알겠지만, 반환타입이 DTO이다!

그런데 사실 기본적으로는 반환타입을 DTO로 지정할 수 없다. from절의 엔티티와 일치하지 않아서… 값을 담아줄 수 없다.

따라서 이럴때는 select 절에 다음처럼 new 라는 키워드를 사용한다.

        public List<OrderSimpleQueryDto> findOrderDtos(){
           return em.createQuery(
                    "select new jpabook.jpashop.repository.OrderSimpleQueryDto() from Order o" +
                            " join o.member m" +
                            " join o.delivery d", OrderSimpleQueryDto.class)
                    .getResultList();
        }

그리고 여기서 끝이 아니라… 생성자의 인자에 값을 줘야 한다.

image.png

이를 위해… 해당 DTO의 생성자를 손봐야 한다…

image.png