오봉이와 함께하는 개발 블로그

JPA 2 - 간단한 주문 조회 V4 : JPA에서 DTO로 바로 조회 본문

BE/JPA

JPA 2 - 간단한 주문 조회 V4 : JPA에서 DTO로 바로 조회

오봉봉이 2022. 9. 5. 00:09
728x90

간단한 주문 조회 V4: JPA에서 DTO로 바로 조회

OrderSimpleApiController - 추가

@GetMapping("/api/v4/simple-orders")
public List<OrderSimpleQueryDto> ordersV4() {
    return orderRepository.findOrderDtos();
}

OrderSimpleQueryRepository 조회 전용 리포지토리

public List<OrderSimpleQueryDto> findOrderDtos() {
    return em.createQuery("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();
}

JPA는 @Entity@Embeddable만 반환할 수 있다.(DTO를 직접 반환할 수 없다.)
그래서 꼭! new Operation을 명시해야 한다.
new Operation을 사용할 때는 from Order oo(엔티티)를 사용하면 식별자로 넘어가기 때문에 파라미터를 직접 넣어줘야 한다.

OrderSimpleQueryDto 리포지토리에서 DTO 직접 조회

@Data
public class OrderSimpleQueryDto {
    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;
    }
}

쿼리

V3

    select
        order0_.order_id as order_id1_6_0_,
        member1_.member_id as member_i1_4_1_,
        delivery2_.delivery_id as delivery1_2_2_,
        order0_.delivery_id as delivery4_6_0_,
        order0_.member_id as member_i5_6_0_,
        order0_.order_date as order_da2_6_0_,
        order0_.status as status3_6_0_,
        member1_.city as city2_4_1_,
        member1_.street as street3_4_1_,
        member1_.zipcode as zipcode4_4_1_,
        member1_.name as name5_4_1_,
        delivery2_.city as city2_2_2_,
        delivery2_.street as street3_2_2_,
        delivery2_.zipcode as zipcode4_2_2_,
        delivery2_.status as status5_2_2_
    from
        orders order0_
    inner join
        member member1_
            on order0_.member_id=member1_.member_id
    inner join
        delivery delivery2_
            on order0_.delivery_id=delivery2_.delivery_id

V4

    select
        order0_.order_id as col_0_0_,
        member1_.name as col_1_0_,
        order0_.order_date as col_2_0_,
        order0_.status as col_3_0_,
        delivery2_.city as col_4_0_,
        delivery2_.street as col_4_1_,
        delivery2_.zipcode as col_4_2_
    from
        orders order0_
    inner join
        member member1_
            on order0_.member_id=member1_.member_id
    inner join
        delivery delivery2_
            on order0_.delivery_id=delivery2_.delivery_id

from절 아래로는 같지만 상위로는 내가 원하는 정보만 select해서 조회한다.

  • 일반적인 SQL을 사용할 때 처럼 원하는 값을 선택해서 조회한다.
  • new 명령어를 사용해서 JPQL의 결과를 DTO로 즉시 변환한다.
  • SELECT 절에서 원하는 데이터를 직접 선택하므로 DB에서 애플리케이션으로 오는 네트워크 용량을 최적화 할 수 있다.
    • row 값 몇개 더 추가하는 것 보다 join 하는 것이 더 자원을 많이 소모하기 때문에 효과는 생각보다 미비하다.
      • 하지만 데이터의 사이즈가 많이 크다면 고민해보자.
  • 리포지토리 메소드 재사용성 떨어진다.
  • API 스펙에 맞춘 코드가 리포지토리에 들어가는 단점이 있다.
    • 리포지토리는 엔티티를 조회하는 용도로 사용해야 하는데, DTO를 조회하는 용도가 추가되어 있기 때문에 구조가 무너진다.
    • API 스펙이 변경되면 리포지토리를 다시 고쳐야 하는 단점.

API 스펙에 맞춘 코드가 리포지토리에 들어가는 단점을 해결하는 방법

// 해당 메소드를 다른 클래스에 구분지어서 만들었다.
package jpabook.jpashop.repository.order.simplequery;

@Repository
@RequiredArgsConstructor
public class OrderSimpleQueryRepository {

    private EntityManager em;

    public List<OrderSimpleQueryDto> findOrderDtos() {
        return em.createQuery("select " +
                        "new jpabook.jpashop.repository.order.simplequery.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로 변환하거나, DTO로 바로 조회하는 두가지 방법은 각각 장단점이 있다.
둘중 상황에 따라서 더 나은 방법을 선택하면 된다.
엔티티로 조회하면 리포지토리 재사용성도 좋고, 개발도 단순해진다.
따라서 권장하는 방법은 다음과 같다.

쿼리 방식 선택 권장 순서

  1. 우선 엔티티를 DTO로 변환하는 방법을 선택한다.
  2. 필요하면 페치 조인으로 성능을 최적화 한다.
    1. 여기서 대부분의 성능 이슈가 해결된다.
  3. 그래도 안되면 DTO로 직접 조회하는 방법을 사용한다.
  4. 최후의 방법은 JPA가 제공하는 네이티브 SQL이나 스프링 JDBC Template을 사용해서 SQL을 직접 사용한다.
인프런 김영한 지식공유자님 강의 - 실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
728x90
Comments