Paging
를 사용하다 보면 이런 경험 없으셨나요? @OneToMany(fetch = FetchType.LAZY)
로 설정했는데도 연관 엔티티들이 마치 EAGER
처럼 즉시 불러와지는 현상,,,, 이게 바로 페이징(Pageable
)과 Lazy 로딩이 만나서 생기는 재미있는(?) 문제랍니다.
Pageable
을 쓰면 JPA가 여러 쿼리를 실행하는데, 이때 Hibernate가 "아, 이거 다 필요하겠지?"하고 성능 최적화한다고 EAGER
처럼 행동해버리는 것입니다.LazyInitializationException
이 튀어나와요. JPA가 이걸 막으려고 데이터를 한 번에 다 가져오려고 하는 것!@EntityGraph
를 붙이면 "이 관계는 즉시 로딩해줘"라고 JPA에게 부탁할 수 있습니다.@BatchSize
어노테이션으로 N+1 문제를 좀 완화시킬 수 있습니다. LazyInitializationException
과 안녕~ 할 수 있습니다.// 페치 조인 사용하기
@Query("SELECT a FROM ApiLogs a LEFT JOIN FETCH a.details WHERE a.id = :id")
Optional<ApiLogs> findByIdWithDetails(@Param("id") Long id);
// @EntityGraph 활용하기
@EntityGraph(attributePaths = {"details"})
Page<ApiLogs> findAll(Pageable pageable);
// BatchSize 설정하기
@Entity
@BatchSize(size = 100)
public class ApiLogs {
@OneToMany(mappedBy = "apiLog", fetch = FetchType.LAZY)
private List<ApiLogDetails> details;
}
// DTO 프로젝션 사용하기
public interface ApiLogSummary {
Long getId();
String getEndpoint();
}
@Query("SELECT a.id as id, a.endpoint as endpoint FROM ApiLogs a")
Page<ApiLogSummary> findAllProjectedBy(Pageable pageable);
Hibernate의 Lazy 로딩과 페이징 사이의 문제, 생각보다 복잡하다. 처음에 분명 Lazy로 설정했는데 n+1쿼리가 쭈르륵 나와서 놀랬다. ,, 휴
fetch join 을 사용하여 해결!!