Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

API 개발 정리 본문

인프런/[인프런] Springboot와 JPA활용 2

API 개발 정리

Seung__ 2021. 8. 25. 23:39

1. 엔티티 조회


  • 엔티티를 조회해서 그대로 반환
  • 엔티티 조회 후 DTO로 변환
  • fetch join으로 쿼리 수 최적화
  • 컬레견 페이징과 한계 돌파
    • 컬렉션은 페티 조인시 페이징이 불가능!!!!
    • ToOne 관계는 페치 조인으로 쿼리 수 최적화하자
      data row에 영향을 안줌
    • 컬렉션은 페치 조인 대신에 지연로딩 유지하고, hibernate.default_batch_fetch_size, @BatchSize

 

2. DTO 직접 조회


  • JPA에서 DTO를 직접 조회
  • 컬렉션 조회 최적화 - 일대다 관계인 컬렉션은 in절을 통해서 메모리에 미리 조회하여 최적화
  • 플랫 테이터 최적화 - join 결과를 그대로 조회 후 애플리케이션에서 원하는 모양으로 직접 변환

 

 

3. 권장 순서


  • 엔티티 조회 방식으로 우선 접근
    • 페치 조인으로 쿼리 수 최적화
    • 컬렉션 최적화
      • 페이징 필요 : hibernate.default_batch_fetch_size, @BatchSize
      • 페이징 노필요 : Fetch join
  • 엔티티 조회 방식으로 해결이 안되면 DTO 조회 방식 사용
  • DTO 조회방식으로 해결이 안되면 Native SQL이나 스프링 Jdbc Template 사용

 

 

4. 참고


  • 엔티티 조회 방식은 페치 조인이나, hibernate.default_batch_fetch_size, @BatchSize 같이 
  • 코드를 거의 수정하지 않고 옵션만 살짝 변경해여 다양한 성능 최적화 시도가 가능.
  • 반면에 dto를 직접 조회하는 방식은 성능을 최적화 하거나 성능 최적화 방식을 변경할 때 
  • 많은 코드를 변경해야함...
  • 개발자는 성능 최적화와 코드 복잡도 사이에서 줄타기 해야함.
  • 항상 그런 것은 아니지만, 보통 성능 최적화는 단순한 코드를 복잡한 코드로 몰고감.
  • 엔티티 조회 방식은 jpa가 많은 부분을 최적화 해주기 때문에 단순한 코드를 유지하면서 성능을 최적화 할 수 있다.
  • 반면에 dto 조회 방식은 sql을 직접 다루는 것과 유사하기 때문에, 둘 사이에 간을 잘 봐야한다 .ㅋㅋ

 

 

 

5. DTo 방식 조회의 선택지


  • DTO를 조회하는 방법에도 장단이 있다.
  • 앞의 글에서 작성한 V4, V5, V6에서 단순하게 쿼리가 한 번만 실행된다고 V6가 좋은 것은 아니다.
  • V4는 코드가 단순함. 만약 특정 주문 한 건만 조회하면 성능 잘나옴.
  • V5는 코드가 좀 더 복잡함. 여러 주문을 한꺼번에 조회하는 경우는 V4 대신에 V5방식을 사용해야함.
  • 예를들어 조회한 ORDER데이터가 1000건인데, V4를 사용하면 쿼리가  1 + 1000이 날라감...
  • V6는 완~~전 다른 방법임. 쿼리 한번으로  최적화 되어서 좋아보이는데,,, Order 기준으로 페이징 불가능.
  • 실무에서는 페이징이 꼭 필요함.. 
  • v4

 

 public List<OrderQueryDto> findOrderQueryDtos(){
        List<OrderQueryDto> result = findOrders();

        result.forEach(o -> {
            List<OrderItemQueryDto> orderItems =  findOrderItems(o.getOrderId());
            o.setOrderItems(orderItems);
        });
        return  result;
    }

 

  • v5

 

public List<OrderQueryDto> findAllByDto_optimization() {
        List<OrderQueryDto> result = findOrders();


        List<Long> orderIds = toOrderIds(result);

        Map<Long, List<OrderItemQueryDto>> orderItemMap = findOrderItemMap(orderIds);

        //메모리에 map을 올려놓고 찾아서 꽂아넣기.
        result.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId())));

        return result;
    }

 

  • v6

 

     @GetMapping("/api/v6/orders")
    public List<OrderQueryDto> ordersV6() {
        List<OrderFlatDto> flats = orderQueryRepository.findAllByDto_flat();

        return flats.stream()
                .collect(groupingBy(o -> new OrderQueryDto(o.getOrderId(), o.getName(), o.getOrderDate(), o.getOrderStatus(), o.getAddress()),
                        mapping(o -> new OrderItemQueryDto(o.getOrderId(), o.getItemName(), o.getOrderPrice(), o.getCount()), toList())
                )).entrySet().stream()
                .map(e -> new OrderQueryDto(e.getKey().getOrderId(), e.getKey().getName(), e.getKey().getOrderDate(), e.getKey().getOrderStatus(), e.getKey().getAddress(), e.getValue()))
                .collect(toList());
    }
    
 public List<OrderFlatDto> findAllByDto_flat() {

        return em.createQuery(
                "select new " +
                        "springjpa2.repository.order.query.OrderFlatDto(o.id, m.name, o.orderDate, o.status, d.address, i.name, oi.orderPrice, oi.count) " +
                        "from Order o " +
                        "join o.member m " +
                        "join o.delivery d " +
                        "join o.orderItems oi " +
                        "join oi.item i", OrderFlatDto.class)
                .getResultList();
    }

 

Comments