일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- springboot
- Kotlin
- 백준
- spring security
- 백준 16719
- Coroutine
- 프로그래머스
- MSA
- 웹어플리케이션 서버
- JPA
- java
- spring oauth
- 프로래머스
- java 기술면접
- 백준 16235
- Spring
- with recursive
- 백준 파이썬
- 백준 19238
- 백준 17626
- JVM
- sql 기술면접
- MySQL
- 백준 15685
- 파이썬
- re.split
- 백준 16236
- 백준 17779
- Spring Boot
- spring cloud
- Today
- Total
시작이 반
[JPA] 1 + n 조회, x To One에서 join fetch 본문
Table 정보
orders
member
delivery
찾을 api 정보 : orders
@GetMapping("/api/v2/simple-orders")
public List<SimpleOrderDto> orderV2(){
//ORDER 2개
List<Order> orders = orderRepository.findAllByString(new OrderSearch());
List<SimpleOrderDto> result = orders.stream()
.map(o -> new SimpleOrderDto(o))
.collect(Collectors.toList());
return result;
}
결과:
api정보
@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 초기화
}
}
LAZY 설정 때문에 쿼리수가 5개가 된다. ( 1+N 문제)
Order 을 처음에 조회한다. 1번 (2개의 결과 : userA, userB 그러면 1+N+N개가 나와야함 여기서 N = 2)
2개를 루프돌면서 확인한다.
1. userA
member을 조회한다. 1번
delivery를 조회한다. 1번
2. userB
member을 조회한다. 1번
delivery를 조회한다. 1번
즉, 1+2+2개의 쿼리가 실행된다.
실행된 쿼리 개수 : 5개
이 문제를 해결하기 위하여
join fetch로 최적화 한다.
@GetMapping("/api/v3/simple-orders")
public List<SimpleOrderDto> orderV3(){
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();
}
이렇게 되면 order를 가져올때 member와 delibery도 한번에 가져온다.
엔티티를 페치 조인(fetch join)을 사용해서 쿼리 1번에 조회
페치 조인으로 order -> member , order -> delivery 는 이미 조회 된 상태 이므로 지연로딩X
실행된 쿼리 개수 : 1개
그런데 api 에 필요없는정보까지 select한다.
api 에 필요한 정보만 select하기 위해서는
레포지토리에서 DTO를 직접 조회한다. ( 안좋은 방법인듯, 재사용성도 떨어지고...)
@GetMapping("/api/v4/simple-orders")
public List<OrderSimpleQueryDto> orderV4(){
return orderRepository.findOrderDto();
}
public List<OrderSimpleQueryDto> findOrderDto() {
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();
}
쿼리 방식 선택 권장 순서
1. 우선 엔티티를 DTO로 변환하는 방법을 선택한다.
2. 필요하면 페치 조인으로 성능을 최적화 한다. 대부분의 성능 이슈가 해결된다.
3. 그래도 안되면 DTO로 직접 조회하는 방법을 사용한다.
4. 최후의 방법은 JPA가 제공하는 네이티브 SQL이나 스프링 JDBC Template을 사용해서 SQL을 직접사용한다.
참고:
'Programming > JPA' 카테고리의 다른 글
[JPA]OSIV (0) | 2021.06.23 |
---|---|
[JPA] One To Many에서 join fetch ( 페이징 ) (1) | 2021.06.07 |
[JPA] xToMany - ManyToX (0) | 2021.05.29 |
[JPA] 변경감지와 병합(merge) (1) | 2021.05.27 |
[JPA] DTO, Domain(Entity) (0) | 2021.02.19 |