Study/실전! 스프링 부트와 JPA 활용2
지연로딩과 조회 성능 최적화
Subi
2022. 7. 29. 14:41
쿼리 방식 선택 권장 순서
1. 우선 엔티티를 DTO 로 변환하는 방법을 선택한다.
2. 필요하면 패치 조인으로 성능을 최적화 한다. (보통 여기서 해결)
3. DTO로 직접 조회하는 방법을 사용
4. JPA가 제공하는 네이티브 SQL이나 스프링 JDBC Template을 사용해서 SQL을 직접 사용한다.
엔티티에 데이터 직접 조회 API
@GetMapping("/api/v1/simple-orders")
public List<Order> orderV1(){
List<Order> all = orderRepository.findAllByString(new OrderSearch());
for (Order order:all){
order.getMember().getName(); // Lazy 강제 초기화
order.getDelivery().getAddress(); // Lazy 강제 초기화
}
return all;
}
- Order Entity 에 직접 조회를 한다.
- 결과물
- Query
- 7개 → 상당히 비 효율적
엔티티를 DTO 로 변환 하는 방법
@GetMapping("/api/v2/simple-orders")
public List<SimpleOrderDto> ordersV2(){
List<Order> orders = orderRepository.findAllByString(new OrderSearch());
List<SimpleOrderDto> result = orders.stream()
.map(SimpleOrderDto::new)
.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.getMember().getAddress(); //LAZY 초기화
}
}
- DTO를 추가하여 Entity에서 조회한 데이터를 DTO 에 담아서 반환
- 결과물
- Query
- 3개 → 안정적이긴 하지만 N + 1 문제
Fetch Join 으로 성능 최적화 조회
@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;
}
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();
}
- Repository에서 Fetch 조인을 사용한 query문으로 조회를 한다.
- 결과물
- Query
- 단 하나의 쿼리로 인해 성능이 좋아짐 (N +1 문제 해결)
DTO로 직접 조회하는 방법을 사용
@GetMapping("/api/v4/simple-orders")
public List<OrderSimpleQueryDto> ordersV4(){
return orderRepository.findOrderDtos();
}
public List<OrderSimpleQueryDto> findOrderDtos() {
return em.createQuery(
"select " +
"new jpabook.jpashop.repository.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();
}
@Data
public class OrderSimpleQueryDto {
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
public OrderSimpleQueryDto(Long orderId, String name, LocalDateTime orderDate, OrderStatus orderStatus, Address address) {
this.orderId = orderId;
this.name = name;
this.orderDate = orderDate;
this.orderStatus = orderStatus;
this.address = address;
}
}
- OrderSimpleQueryDto Class 를 생성 (Repository가 클래스를 의존하면 안되는 문제로 따로 생성)
- join을 사용해 Repository에서 데이터 Query문을 실행 할 때, 생성한 OrderSimpleQueryDto 의 생성자를 불러와 조회하는 데이터 최소화
- 결과물
- Query
- 성능은 가장 뛰어나나, 크게 체감적으로 느껴질만한 부분이 아님
강의 : 인프런 실전! 스프링 부트와 JPA 활용2 (김영한) https://inf.run/5NP4
실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화 - 인프런 | 강의
스프링 부트와 JPA를 활용해서 API를 개발합니다. 그리고 JPA 극한의 성능 최적화 방법을 학습할 수 있습니다., - 강의 소개 | 인프런...
www.inflearn.com
[Study/실전! 스프링 부트와 JPA 활용2] - Inner Join 과 Fetch Join 의 차이
Inner Join 과 Fetch Join 의 차이
💡 Fetch Join 을 사용할 때 발생하는 쿼리는 Inner Join 의 쿼리와 차이가 없는데 왜 Fetch Join의 성능이 좋을까? Inner Join 오직 JPQL에서 조회하는 주체가 되는 Entity만 조회하여 영속화 Fetch Join 조회의..
dhstroy.tistory.com