Entity를 DTO로 변환

컴공생의 코딩 일기·2023년 1월 31일
0

JPA

목록 보기
11/14
post-thumbnail
post-custom-banner

Entity를 DTO로 변환

@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {

    private final OrderRepository orderRepository;


    @GetMapping("/api/v2/simple-orders")
    public List<SimpleOrderDto> orderV2(){
        List<Order> orders = orderRepository.findAllByString(new OrderSearch());
        List<SimpleOrderDto> result =
                orders.stream().map(SimpleOrderDto::fromEntity).collect(Collectors.toList());
        return result;
    }

// Entity를 Dto로 변환하는 방법중
// Record(Data Class)를 사용했습니다
    public record SimpleOrderDto(
            Long orderId,
            String name,
            LocalDateTime orderDate,
            OrderStatus orderStatus,
            Address address
    ){

        public static SimpleOrderDto fromEntity(
                Order order
        ){
            return new SimpleOrderDto(
                    order.getId(),
                    order.getMember().getName(), // Lazy 초기화
                    order.getOrderDate(),
                    order.getStatus(),
                    order.getDelivery().getAddress() // Lazy 초기화
            );
        }

    }
}

Entity를 직접 노출하는 것은 매우 매우 좋지 않기 때문에 Dto 클래스로 변환 후 화면에 노출시켜야 한다. Dto로 변환할 경우 데이터 베이스에 테이블이 변경되도 API 스펙에는 영향을 주지 않기 때문에 Dto로 변환해서 사용해야 한다.

  • 위에 코드를 실행할 경우 쿼리가 총 1 + N + N번 실행된다.
    • order 조회 1번 (order 조회결과 수가 N이 된다.)
    • order -> member 지연로딩 조회 N번
    • order -> delivery 지연로딩 조회 N번
    • 예) order의 결과가 4개일 경우 최악의 경우 1 + 4 + 4번 실행된다.
      • 지연로딩은 영속성 컨텍스트에서 조회하므로, 이미 조회된 경우 쿼리를 생략한다.

해결 방법 (Fetch Join 사용)

 public List<Order> findAllWithMemberDelivery(OrderSearch orderSearch) {
        return em.createQuery("select o from Order o" +
                " join fetch o.member m" +
                " join fetch o.delivery d", Order.class)
                .getResultList();
    }
@GetMapping("/api/v3/simple-orders")
    public List<SimpleOrderDto> orderV3(){
        List<Order> orders = orderRepository.findAllWithMemberDelivery(new OrderSearch());
        List<SimpleOrderDto> result =
                orders.stream().map(SimpleOrderDto::fromEntity).collect(Collectors.toList());
        return result;
    }
select
        order0_.order_id as order_id1_6_0_,
        member1_.memer_id as memer_id1_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_.memer_id 
    inner join
        delivery delivery2_ 
            on order0_.delivery_id=delivery2_.delivery_id

Fetch Join 을 사용할 경우 쿼리를 한번만 조회하면 되기 때문에 성능상 훨씬 좋다.

profile
더 좋은 개발자가 되기위한 과정
post-custom-banner

0개의 댓글