Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- Thymeleaf
- transaction
- 그리디
- Proxy
- db
- 스프링 핵심 기능
- Greedy
- 백준
- Spring Boot
- 스프링 핵심 원리
- 김영한
- Android
- 스프링
- pointcut
- jpa
- http
- Exception
- SpringBoot
- AOP
- JDBC
- Servlet
- 자바
- JPQL
- springdatajpa
- 알고리즘
- spring
- kotlin
- java
- 인프런
- QueryDSL
Archives
- Today
- Total
개발자되기 프로젝트
API 개발 정리 본문
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();
}
'인프런 > [인프런] Springboot와 JPA활용 2' 카테고리의 다른 글
스프링 데이터 JPA 소개 (0) | 2021.08.26 |
---|---|
OSIV와 성능 최적화 (0) | 2021.08.26 |
컬렉션 조회 최적화 : 플랫 데이터 최적화 (0) | 2021.08.25 |
컬렉션 조회 : 최적화 (0) | 2021.08.25 |
컬렉션 조회: DTO 직접 조회 (0) | 2021.08.24 |
Comments