페치조인, 페이징
1. 페이징
엔티티 조회
* 선호하는 방법 중 하나
@GetMapping("/api/v3-1/orders")
public ResponseEntity<Result<List<OrderDto>>> orderV3_page(
@RequestParam(value = "offset", defaultValue = "0") String offset,
@RequestParam(value = "limit", defaultValue = "100") String limit
) {
// N 만큼 데이터 나옴
List<Order> orders = repository.findAllWithMemberDelivery(offset, limit); // ToOne 관계이기 때문에 페이징 가능
List<OrderDto> result = orders.stream()
.map(o -> new OrderDto(o))
.toList();
return ResponseEntity.ok(new Result<>(result));
}
컬렉션을 페치 조인하면 페이징이 불가능
Hibernate:
select
distinct o1_0.order_id,
d1_0.delivery_id,
d1_0.city,
d1_0.street,
d1_0.zipcode,
d1_0.status,
m1_0.member_id,
m1_0.city,
m1_0.street,
m1_0.zipcode,
m1_0.name,
o1_0.order_date,
o2_0.order_id,
o2_0.order_item_id,
o2_0.count,
i1_0.item_id,
i1_0.dtype,
i1_0.name,
i1_0.price,
i1_0.stock_quantity,
i1_0.artist,
i1_0.etc,
i1_0.author,
i1_0.isbn,
i1_0.actor,
i1_0.director,
o2_0.order_price,
o1_0.status
from
orders o1_0
join
member m1_0
on m1_0.member_id=o1_0.member_id
join
delivery d1_0
on d1_0.delivery_id=o1_0.delivery_id
join
order_item o2_0
on o1_0.order_id=o2_0.order_id
join
item i1_0
on i1_0.item_id=o2_0.item_id
한번의 쿼리로 조회
단점 : 페이징 불가능
참고: 컬렉션 페치 조인을 사용하면 페이징이 불가능하다.
하이버네이트는 경고 로그를 남기면서 모든 데이터를 DB에서 읽어오고(위험함), 메모리에서 페이징 해버린다(매우 위험하다).
Order의 기준자체가 틀어져버림
자세한 내용은 자바 ORM 표준 JPA 프로그래밍의 페치 조인 부분을 참고하자.
참고: 컬렉션 페치 조인은 1개만 사용할 수 있다. 컬렉션 둘 이상에 페치 조인을 사용하면 안된다. 데이터가 부정합하게 조회될 수 있다.
자세한 내용은 자바 ORM 표준 JPA 프로그래밍을 참고하자
주문 조회 V3: 엔티티를 DTO로 변환 - 페치 조인 최적화
19:33~
페이징 방법
1. ToOne (OneToOne, ManyToOne) 관계는 모두 페치조인
ToOne 관계인 delivery, member는 페치조인이나 한방쿼리
페이징에 영향주지 않음
public List<Order> findAllWithMemberDelivery(String offset, String limit) {
return em.createQuery(
"SELECT o FROM Order o" +
" JOIN FETCH o.member m" +
" JOIN FETCH o.delivery d", Order.class)
.setFirstResult(Integer.parseInt(offset))
.setMaxResults(Integer.parseInt(limit))
.getResultList();
// SELECT o FROM Order o
// 이렇게 하는 것보다 member, delivery 에 나가는 쿼리를 줄일 수 있다.
}
2. 컬렉션은 지연 로딩으로 조회
3. 지연 로딩 성능 최적화
hibernate.default_batch_fetch_size : 글로벌 설정
@BatchSize : 특정 최적화
컬렉션, 프록시 객체를 한꺼번에 설정한 size 만큼 in 쿼리로 조회
갯수만큼 미리 당겨옴
@Getter
static class OrderDto {
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
private List<OrderItemDto> orderItems;
public OrderDto(Order order) {
orderId = order.getId();
name = order.getMember().getName();
orderDate = order.getOrderDate();
orderStatus = order.getStatus();
address = order.getDelivery().getAddress();
// ToMany관계, 조회시 지연로딩(Lazy)으로 조회 - 프록시 초기화 되면서 가져옴
orderItems = order.getOrderItems().stream()
.map(orderItem -> new OrderItemDto(orderItem))
.toList();
}
}
@Getter
static class OrderItemDto {
private String itemName;
private int orderPrice;
private int count;
public OrderItemDto(OrderItem orderItem) {
itemName = orderItem.getItem().getName();
orderPrice = orderItem.getOrderPrice();
count = orderItem.getOrderPrice();
}
}
배치 사이즈 설정
application.yml
spring:
jpa:
show_sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
default_batch_fetch_size: 100 # ToMany 관계 페이징 + 성능최적화 ( where in 으로 조건 쿼리)
N + 1 문제 해결 (in 쿼리로 인해)
지연로딩으로 OrderItem 을 가져올 때
orderItems = order.getOrderItems()
.stream().map(orderItem -> new OrderItemDto(orderItem)).toList();
개별적용도 가능
@BatchSize(size = 1000) // 개별 batch size 설정 (in 쿼리)
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
효과
적용 전
적용 후
성능을 더 올리고 싶다면 -> Redis
장점
- 쿼리 호출 수가 1+N → 1+1 로 최적화 된다.
- DB데이터 전송량이 최적화 , 각각 조회하므로 전송해야할 중복 데이터가 없다.
- 페치 조인 방식과 비교해서 쿼리 호출 수가 약간 증가하지만, DB 데이터 전송량이 감소
- 페이징 가능
ToOne 관계는 페치 조인으로 쿼리 수를 줄이고
나머지는 "hibernate.default_batch_fetch_size" 로 최적화
size는 100~1000사이 권장 (500)
SQL IN 절을 사용하기에, 1000개 이상일 때 오류를 일으니키는 DB가 있음.
item 또한 IN 쿼리를 날리면서
쿼리 호출 수가 1:N:N 쿼리가 1:1:1 로 최적화 된다.
order : orderItem: Item
+ 스프링부트 3.1 변경사항
스프링 부트 3.1 부터는 하이버네이트 6.2를 사용한다. 하이버네이트 6.2 부터는 where in 대신에 array_contains 를 사용한다
'Back-end > Spring Boot + REST API' 카테고리의 다른 글
스프링부트 API JPA 최적화 (N+1) 컬렉션 조회 최적화, DTO 조회 성능 향상 (0) | 2023.12.21 |
---|---|
스프링부트 API JPA 최적화 ToOne 관계 (N+1 문제) , 페치 조인 (0) | 2023.12.19 |
Spring Boot 시큐리티 + JWT 흐름 정리 (0) | 2022.11.23 |
Spring Boot 실행 프로세스와 Embedded Servers , CLI 실행 (0) | 2022.09.09 |
Spring boot - blog application (REST API) : AWS RDS, Elastic Beanstalk (0) | 2022.09.08 |
댓글