지연로딩, 즉시로딩
Eager
JOIN 문으로 관련된 모든 테이블 데이터 가져옴
단, 쿼리 결과 예상을 못함
Lazy`
Proxy
실제 클래스를 상속 받아 만들어진 것으로 사용자 입장에서는 진짜 객체랑 구분하지 않고 사용.
프로시 객체를 호출하면 실제 객체 메소드 호출
단, N+1 성능문제
JOIN 문으로 관련된 모든 테이블 데이터 가져옴
단, 쿼리 결과 예상을 못함
Proxy
실제 클래스를 상속 받아 만들어진 것으로 사용자 입장에서는 진짜 객체랑 구분하지 않고 사용.
프로시 객체를 호출하면 실제 객체 메소드 호출
단, N+1 성능문제
Order 조회 -> 조회결과 2건
Member, Delivery 테이블 2번씩 조회, 총 5번 조회(최악의 경우)
단, 만약 같은 memer 혹은 delivery 라면 영속성 컨텍스트에서 가져오니까 최악 경우보다는 낫다
@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)) // SimpleOrderDto::new
.collect(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(); // 이 때 쿼리 나감
orderDate = order.getOrderDate();
orderStatus = order.getStatus();
address = order.getDelivery().getAddress(); // 이 때 쿼리 나감
}
}
지연로딩 쿼리 결과 N+1 문제
select
order0_.order_id as order_id1_6_,
order0_.delivery_id as delivery4_6_,
order0_.member_id as member_i5_6_,
order0_.order_date as order_da2_6_,
order0_.status as status3_6_
from
orders order0_
select
member0_.member_id as member_i1_4_0_,
member0_.name as name5_4_0_
from
member member0_
where
member0_.member_id=?
select
delivery0_.delivery_id as delivery1_2_0_,
delivery0_.city as city2_2_0_,
delivery0_.street as street3_2_0_,
from
delivery delivery0_
where
delivery0_.delivery_id=?
select
member0_.member_id as member_i1_4_0_,
member0_.name as name5_4_0_
from
member member0_
where
member0_.member_id=?
select
delivery0_.delivery_id as delivery1_2_0_,
delivery0_.city as city2_2_0_,
delivery0_.street as street3_2_0_,
from
delivery delivery0_
where
delivery0_.delivery_id=?
@BatchSize(size)
lazyLoading 시 연관된 엔티티를 조회할 때 지정된 size 만큼 SQL의 IN절을 사용해서 조회한다
spring.jpa.properties.hibernate.default_batch_fetch_size: 1000
select
order0_.order_id as order_id1_6_,
order0_.delivery_id as delivery4_6_,
order0_.member_id as member_i5_6_,
order0_.order_date as order_da2_6_,
order0_.status as status3_6_
from
orders order0_
inner join
member member1_
on order0_.member_id=member1_.member_id limit ?
Hibernate:
select
member0_.member_id as member_i1_4_0_,
member0_.city as city2_4_0_,
member0_.street as street3_4_0_,
member0_.zipcode as zipcode4_4_0_,
member0_.name as name5_4_0_
from
member member0_
where
member0_.member_id in (
?, ?
)
Hibernate:
select
delivery0_.delivery_id as delivery1_2_0_,
delivery0_.city as city2_2_0_,
delivery0_.street as street3_2_0_,
delivery0_.zipcode as zipcode4_2_0_,
delivery0_.status as status5_2_0_
from
delivery delivery0_
where
delivery0_.delivery_id in (
?, ?
)
Fetch Join
연관된 테이블을 가져오지만, 프록시 객체가 아닌 실제 객체를 가져온다. 다만, 1:N 관계에서 가져올 떄 다음과 같은 데이터 뻥튀기 문제가 있어서 distinct로 다음과 같이 중복된 entity는 제거한다
public List<Order> findAllWithItem() {
return em.createQuery(
"select distinct o from Order o" +
" join fetch o.orderItems oi" +
" join fetch oi.item i", Order.class)
.getResultList();
}
한계
- 1:N 경우, 데이터 뻥튀기가 되어서 컬렉션을 페치 조인하면 페이징 API(setFirstResult,
setMaxResults)를 사용할 수 없다. distinct전에 페이징 쿼리 나간다 - 일대일, 다대일 같은 단일 값 연관 필드들은 페치 조인해도 페이징 가능
- db 쿼리에서 limit, offset 쿼리가 없다. 하이버네이트는 경고 로그를 남기고 메모리에서 페이징(매우 위험) out of memory 위험성 -> team => member가 아닌 member => team으로 방향을 바꾼다
Reference
김영한 강사님 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 강의
Author And Source
이 문제에 관하여(지연로딩, 즉시로딩), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@minsuk/지연로딩-즉시로딩저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)