JPA N+1 문제 해결과정 ( join fetch + CountQuery)

sujin·2023년 5월 17일
0

들어가면서

JPA를 사용해서 개인프로젝트를 진행하면서 발생한 N+1문제와 이를 해결하면서 겪은 오류에 대해 정리해보려고합니다. 간단한 오류해결 문제만 정리를 하려고 하니 이론에 대해 자세히 알고싶으신 분들은 이 링크(아직작성되지않음)를 먼저 읽고 오시면 좋을것 같습니다.


N+1 문제 상황

JPA를 사용하는 경우 xxxToOne같은 어노테이션으로 엔티티 관계가 형성되어 있을때 불필요한 쿼리가 더 수행되는 경우가 발생합니다.


아래 코드를 보면 게시물(PostEntity)은 사용자(UserEntity) 한 명이 작성할 수 있는 경우 ManyToOne의 엔티티 관계로 형성되어 있는 것을 알 수 있습니다.
public class PostEntity {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private UserEntity user;

}

만약 전체 글 목록을 출력하고 싶어 JPA의 findAll 메소드를 사용하여 Post를 조회하게 된다면, Post 엔티티 별로 각각 User 엔티티를 또 조회하게 됩니다.
여기서 만약 ? 작성된 글이 5개라면 Post를 조회하는 쿼리와 각각 Post 엔티티에 해당하는 User 엔티티를 조회하기 때문에 추가로 5개의 쿼리가 수행이 됩니다.


N+1 문제 해결

해당 문제는 join fetch 키워드를 사용하여 해당 문제를 해결할 수 있습니다.


`Query` 어노테이션과 JPQL을 사용하여 fetch join 쿼리를 작성하면 되고, fetch 조인은 inner join 처리가 됩니다.
@Query("select p from PostEntity p join fetch p.user")
Page<PostEntity> findAllJoinFetch(Pageable pageable);

QueryException 해결

위 처럼 페이징을 위한 쿼리로 fetch 조인을 사용하게 되면 CountQuery를 정상적으로 만들어주지 못하기 때문에 아래와 같은 오류가 발생합니다.

Reason: Count query validation failed for method public abstract org.springframework.data.domain.Page com.couple.sns.domain.post.persistance.repository.PostRepository.findAllJoinFetch(org.springframework.data.domain.Pageable)

페이징 처리를 위해 전체 카운터를 처리하는 countQuery가 작성되어있지않으면 JPA에서 임의로 원본 쿼리를 보고 countQuery를 생성을 합니다. fetch 조인을 사용하게 되면 해당 부분에 대한 오류가 발생하기 때문에 countQuery를 분리해서 사용해야한다고 합니다.


참고문서

JPA N+1 문제 및 해결방안

JPA Fetch 조인(join)과 페이징(paging) 처리

0개의 댓글