주문 조회 V4: JPA에서 DTO 직접 조회

JPA에서 DTO 직접 조회

OrderApiController 내부에 만든 OrderDto를 재사용하지 않고 새로 만든 이유

Repository에서 Controller의 클래스를 참조하게 될 경우 Repository가 Controller를 참조하면 순환이 생겨버린다‼️(Controller->Repository 순방향)

그래서 Repository > order > query 하위에 아래와 같이 따로 빼내서 만든다!

  • OrderItemQueryDto //OrderItemDTO

  • OrderQueryDto //OrderDTO //orderId 없어도 되지만 일단 만듦. 화면에 렌더링하는 용도의 데이터라서 나중에 안 쓰려면 @JsonIgnore 처리해주면 됨.

  • OrderQueryRepository : EntityManager로 쿼리 작성

OrderQueryRepository 쿼리 작성⭐️⭐️⭐️⭐️⭐️ 1. findOrders() //Order 조회 : Member와 Delivery 조인해서 OrderDTO 생성해서 반환(OrderQueryDto) ⭐️ 이때 new 연산자로 컬렉션을 바로 받지 못한다! DB는 플랫하게 한 row씩 데이터가 들어가기 때문이다. 그래서 컬렉션은 따로 처리해주어야한다. 2. findOrderItems(Long orderId) // OrderItem 조회 : OrderItem 과 Item 조인해서 OrderItemDTO 생성해서 반환(OrderItemQueryDto) 3. findOrderQueryDtos() //메인 메서드. 핵심 로직⭐️⭐️⭐️⭐️⭐️⭐️ : OrderDTO에 List<OrderItemDTO> 컬렉션을 넣는다! 2에서 조회한 OrderItemDTO를 foreach문으로 컬렉션에 넣는다!!!

=>하지만 이코드에서 역시 N+1문제가 발생한다!!!!! findOrderQueryDtos > findOrders() 1번,//주문 1번 foreach문에서 데이터 갯수 N개 만큼 N번//주문 상품 갯수만큼(여기서는 N = 2번)

  1. findOrders() JPQL select절에서 new 연산자로 DTO 생성해서 반환 시, select 절에 new 연산자로 바로 컬렉션을 넣을 수 없다‼️ (참고)일반 SQL 쓸 때 생각해보면 된다. (SQL처럼 쓰는데 new로 DTO를 할 뿐.) =>플랫하게 한줄밖에 못 넣는다. OrderItems일대다 관계로 데이터가 증가하기 때문에 select절에 new로 바로 못 넣는다‼️ 컬렉션 List<OrderItem>만 제외하고 new로 DTO 생성(컬렉션은 따로 넣어주는 처리!)⭐️⭐⭐️

public List<OrderQueryDto> findOrders(){
    return em.createQuery(
            "select new jpabook_v2.jpashop_v2.repository.order.query.OrderQueryDto(o.id, m.name, o.orderDate, o.status, d.address" +
                    " from Order o" +
                    " join o.member  m" +
                    " join o.delivery d", OrderQueryDto.class)
            .getResultList();
}

2. findOrderItems(Long orderId) OrderItem과 Item은 다대일(ManyToOne) 관계이므로 조인해도 된다!!!

private List<OrderItemQueryDto> findOrderItems(Long orderId) {
    return em.createQuery("select new jpabook_v2.jpashop_v2.repository.order.query.OrderItemQueryDto(oi.order.id, i.name, oi.orderPrice, oi.count)"+
            " from OrderItem oi"+
            " join oi.item i"+
            " where oi.order.id = : orderId", OrderItemQueryDto.class)
            .setParameter("orderId",orderId)
            .getResultList();
}

3. 핵심로직 : OrderItemDTO를 OrderDTO에 List<OrderItemDTO> 컬렉션에 넣기 findOrderItems로 조회한 List<OrderItemDTO>를 foreach문으로 하나하나 OrderDTO의 List<OrderItemDTO>에 넣는다!!!!!!!!!

public List<OrderQueryDto> findOrderQueryDtos(){
    List<OrderQueryDto> result = findOrders();
    result.forEach(o -> {
        List<OrderItemQueryDto> orderItems = findOrderItems(o.getOrderId());//컬렉션은 제외하고 직접 채우고 있다.
        o.setOrderItems(orderItems);
    });
    return result;
}

Last updated