반응형
250x250
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
- 정렬
- 컬렉션 조회 최적화
- querydsl
- 서브쿼리
- 친절한 SQL 튜닝
- 자바의 정석
- index
- 페치조인
- SQL 튜닝
- 데이터모델링
- @MappedSuperclass
- fetch join
- Spring data JPA
- TCP/IP
- JPQL
- Spring
- 데이터베이스
- 스프링
- 엔티티
- 오라클
- INDEX SCAN
- JPA
- 성능최적화
- 페이징
- 그룹함수
- SQL
- 값 타입
- DTO
- 스프링 컨테이너
- 스프링 데이터 JPA
Archives
- Today
- Total
nu_s
[JPA 활용] 조회 성능 최적화 - 엔티티를 DTO로 변환(페치 조인) 🐱 본문
728x90
반응형
V2. 엔티티를 DTO로 변환
@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {
private final OrderRepository orderRepository;
@GetMapping("/api/v2/simple-orders")
public List<SimpleOrderDto> ordersV2() {
List<Order> orders = orderRepository.findAllByString(new OrderSearch());
List<SimpleOrderDto> result = orders.stream()
.map(o -> new SimpleOrderDto(o)) // Order를 SimpleOrderDto로 변환
.collect(Collectors.toList());
return result;
}
// DTO
@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.getOrderStatus();
address = order.getDelivery().getAddress(); //LAZY 초기화
}
}
}
DTO를 통해 반환했을 때의 장점
- 엔티티가 바뀌어도 API의 스펙이 깨지지 않는다.
문제점
- 지연 로딩으로 인한 데이터베이스 쿼리가 많이 호출된다.
- 쿼리가 N + 1번 실행된다. (성능 저하)
- order 조회 1번 (order의 조회 결과 수가 N이 된다.)
- order -> member 지연 로딩 조회 N번
- order -> delivery 지연 로딩 조회 N번
- 예) order의 결과가 2개면 1 + 2 + 2번 실행된다. (최악의 경우)
- 지연 로딩은 영속성 컨텍스트에서 조회하므로, 이미 조회된 경우 쿼리를 생략한다.
- 예) 같은 회원이 주문한 경우, 이미 조회된 회원이기 때문에 쿼리가 줄어든다.
해결 방법
- 페치 조인을 사용하면 N + 1 문제를 해결할 수 있다.
V3. 엔티티를 DTO로 변환 - 페치 조인 최적화
- N + 1 문제를 해결하기 위해 페치 조인을 사용하기
@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {
private final OrderRepository orderRepository;
@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;
}
}
OrderRepository
public List<Order> findAllWithMemberDelivery() {
em.createQuery(
"select o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d", Order.class
).getResultList();
}
- V2와 코드는 같지만 리포지토리에서 페치 조인을 사용해준다.
실행 쿼리
select
order0_.order_id as order_id1_6_0_,
member1_.member_id as member_i1_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_ad2_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_.member_id
inner join
delivery delivery2_
on order0_.delivery_id=delivery2_.delivery_id
그러나 쿼리는 단 한 번만 실행된다.
장점
- 엔티티를 페치 조인을 사용해서 쿼리 1번에 조회할 수 있다.
- 페치 조인으로 order -> member, order -> delivery는 이미 조회된 상태이므로 지연로딩이 발생하지 않는다.
단점
- 엔티티의 모든 속성(불필요한 것 포함)을 가져온다.
출처 : 실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
728x90
반응형
'JPA' 카테고리의 다른 글
[JPA 활용] 컬렉션 조회 최적화 - 엔티티를 DTO로 변환 🐱 (0) | 2023.11.10 |
---|---|
[JPA 활용] 조회 성능 최적화 - JPA에서 DTO로 조회🐱 (0) | 2023.11.10 |
[JPA 활용] 조회 성능 최적화 - 엔티티 직접 노출🐱 (0) | 2023.11.04 |
[JPA 활용] API 개발 (포스트맨) 🐱 (0) | 2023.10.26 |
[JPA] JPQL 🐶 (0) | 2023.10.26 |