본문 바로가기
개발/JPA

[JPA] 지연로딩과 조회 성능 최적화 2 - fetch join, DTO로 조회

by Allonsy 2022. 4. 19.
반응형

1. fetch join 을 이용해서 entity 조회 후 DTO로 변환

1 + N 문제 해결도 하고, 조인을 통해 쿼리 한번으로 조회 가능

- Controller 코드 (엔티티로 조회해 왔기 때문에 DTO로 수정 후 반환)

@GetMapping("/api/v3/simple-orders")
public List<SimpleOrderDto> ordersV3() {
    List<Order> orders = orderRepository.findAllWithMemberDelivery();
    List<SimpleOrderDto> result = orders.stream()
            .map(o->new SimpleOrderDto(o))
            .collect(Collectors.toList());
    return result;
}

@Data
static class SimpleOrderDto {
    private Long orderId;
    private String name;
    private LocalDateTime orderDate;
    private OrderStatus orderStatus;
    private Address address;

    public SimpleOrderDto(Order order) {
        orderId = order.getId();
        name = order.getMember().getName(); // LAZY 초기화
        orderDate = order.getOrderDate();
        orderStatus = order.getStatus();
        address = order.getDelivery().getAddress(); // LAZY 초기화
    }
}

- Repository 코드

public List<Order> findAllWithMemberDelivery() {
    return em.createQuery(
            "select o from Order o " +
                    " join fetch o.member m " +
                    " join fetch o.delivery d", Order.class)
            .getResultList();
}

- JPA가 만들어 준 쿼리 (엔티티에 있는 모든 컬럼 조회)

    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_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_delivery_id=delivery2_.delivery_id

2. DTO를 이용해서 필요한 컬럼만 뽑은 DTO로 조회

- Controller 코드 (DTO로 조회해왔기 떄문에 바로 반환 가능)

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

- Repository 코드 ( DTO 코드 생략, select 문에 new 이용하여 full package명을 붙여서 생성자로 DTO 생성 )

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();
}

- JPA가 만들어 준 쿼리 ( DTO에 있는 컬럼만 조회)

    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_delivery_id=delivery2_.delivery_id

 

# 생각할 점

1번과 2번은 트레이드오프가 있음!

1번이 좀 더 깔끔.

2번은 화면 계층과 연관이 있고 엔티티 기준으로 쿼리를 수정하기에 좋지 않음(재사용 힘듬).

2번이 좀 더 성능이 빠를 수 있으나 큰 차이 없음.

확실하게 알아두려면 성능테스트 해봐야함!

 

인프런 - 실전! 스프링 부트와 JPA 활용2 - API개발과 성능최적화(김영한 강사님) 강의를 수강하며 정리한 글입니다 :)

반응형

댓글