| 구분 | 설명 |
|---|---|
| Lazy Loading (지연 로딩) | 실제로 참조할 때 DB 쿼리가 실행됨. Proxy 객체로 지연시킴 |
| Eager Loading (즉시 로딩) | 연관된 엔티티를 항상 함께 조회 (JOIN 쿼리로 즉시 로딩) |
LAZY로 설정하고, 필요한 경우만 명시적으로 패치(fetch)하는 것이 권장됨val order = orderRepositoryfindById(1L)
val member = order.member // 여기서는 프록시 객체
print(member.name) // 이때 실제 SELECT 발생
프록시(proxy)로 로딩됨SELECT 쿼리가 발생하고 값을 사용할 수 있게됨val orders = orderRepository.findAll()
for(order in orders) {
println(order.member.name) // 각 order 엔티티마다 member 조회 SELECT 발생
}
Fetch Join
@Query("select o from Order o join fetch o.member")
fun findAllWithMember(): List<Order>
1:N 관계에서 fetch join은 N이 아닌 1 쪽에서만 사용(N쪽 fetch join은 1 쪽의 엔티티 중복을 일으킴)Batch Size 설정
// 1. @BatchSize
@OneToMany(mappedBy = "order"
@BatchSize(size = 100)
val orderItems: List<OrderItem> = mutableListOf()
// 2. hibernate.default_batch_fetch_size(yaml)
spring:
jpa:
properties:
hibernate.default_batch_fetch_size: 100
in 절로 한 번에 조회하게 하는 설정fetch join보다 유연함을 가지고 페이징에도 안전N + 1 완화 기법EntityGraph
@EntityGraph(attributePaths = ["member", "delivery"]
@Query("select o from Order o")
fun findAllWithMemberAndDelivery(): List<Order>
fetch join과 달리 JPQL 변경 없이 가능fetch할 때 사용하기 좋음 또한 JPA 표준이라 Hibernate 외 구현체에도 동일하게 동작DTO Projection
@Query("""
select new com.example.dto.OrderDto(o.id, m.name, d.address)
from Order o
join o.member M
join o.delivery d
""")
fun findOrderDtos(): List<OrderDto>
fetch join, group by, pagination을 직접 제어batch size + lazy로 관리