N + 1
연관 관계가 설정된 엔티티를 조회할 경우 조회된 데이터 개수(N)개 만큼 연관관계의 조회 쿼리가 추가로 발생하는 현상
findAll 메서드
글로벌 패치 전략을 즉시로딩으로 설정 후 findAll메서드 실행 시
- N + 1 문제 발생
- findAll은
select u from User u라는 JPQL 구문을 생성해서 실행하기 때문 - JPQL은 글로벌 패치 전략을 고려하지 않고 쿼리 실행
- 모든 User를 조회하는 쿼리 실행 후 즉시 로딩 설정을 보고 연관관계 엔티티를 모두 조회
N + 1 해결법
fetch join
연관관계 있는 엔티티를 한번에 즉시로딩하는 구문
@Query("select distinct p from Post p join fetch p.comments")
List<Post> findWithPagination(Pageable pageable);
위와 같이 1 : N 관계의 컬렉션을 fetch join하면서 동시에 Pagination API를 사용하면 OutOfMemoryError가 발생할 수 있기 때문에, 이 둘을 동시에 사용해서는 안된다.
N : 1 관계일 때 사용
List<Comment> comments = entityManager
.createQuery("select c from Comment c join fetch c.post", Comment.class)
.setFirstResult(0)
.setMaxResults(10)
.getResultList();
즉 ~ToOne 관계일 때는 fetch join이 괜찮지만 ~ToMany 관계일 때는 Batch Size 지정
spring.jpa.properties.hibernate.default_batch_fetch_size=1000
@EntityGraph
fetch join과 비슷하며 쿼리 메소드에 해당 어노테이션을 추가해 사용
left outer join 만을 지원한다.
@Override
@EntityGraph(attributePaths = {"team"}) // 옵션 : 연관된 엔티티 지정
List<Member> findAll();
Pagination
한 페이지에 N개의 데이터만 보여주고 추가 요청이 있을 때 다음 순번의 N개 데이터를 보여주는 방식
UX 및 리소스 측면의 단점 보완
JPA Pagination API
DB 벤더에 따라 페이징 처리하는 쿼리가 달라진다.
MYSQL : LIMIT, OFFSET
ORACLE : 복잡한 쿼리로 페이지네이션 처리
@DisplayName("간단한 페이징을 적용해본다.")
@Test
void usePagination() {
EntityManager entityManager = testEntityManager.getEntityManager();
List<Post> posts = entityManager.createQuery("select p from Post p", Post.class)
.setFirstResult(0)
.setMaxResults(10)
.getResultList();
}
Spring DATA JPA
Pageable 구현체를 쿼리 메서드의 파라미터로 전달함으로써 쿼리에 페이징을 동적으로 추가
@Query("select p from Post p")
List<Post> findWithPagination(Pageable pageable);
@DisplayName("Pageable을 사용하여 페이징 처리한다.")
@Test
void pagination() {
postRepository.findWithPagination(Pageable.ofSize(10));
postRepository.findWithPagination(PageRequest.of(0, 2));
}
- 페이징 정렬 조건 까지 함께 지정 가능
일반적인 JPA 사용법
- 모든 연관관계는 기본적으로 지연 로딩(Lazy Loading)으로 설정합니다.
- N+1 문제가 발생하는 특정 조회 로직에서는 Fetch Join이나 @EntityGraph를 사용하여
Post와Comment를 처음부터 함께 조회하도록 쿼리를 최적화합니다. 이는 개발자가 필요한 데이터를 명시적으로 한 번의 쿼리로 가져오게 하여 N+1 문제를 근본적으로 해결하는 방법입니다.