들어가면서
JPA를 사용해서 개인프로젝트를 진행하면서 발생한 N+1문제와 이를 해결하면서 겪은 오류에 대해 정리해보려고한다. 간단한 오류해결 문제만 정리를 하려고 하니 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 조인을 사용하게 되면 해당 부분에 오류가 발생하기 때문에 fetch join 이나 복잡한 쿼리의 경우 countQuery를 분리해서 사용해줘야한다고한다.
@Query(value = "select p from PostEntity p join fetch p.user",
countQuery = "SELECT COUNT(DISTINCT p) FROM PostEntity p INNER JOIN p.user")
Page<PostEntity> findAllJoinFetch(Pageable pageable);
참고문서
'spring 🍀 > spring-data-jpa' 카테고리의 다른 글
Spring Data JPA Auditing으로 엔티티의 생성/수정 시각 자동으로 기록 + 생성자/수정자도 기록하기 (1) | 2023.11.23 |
---|---|
@MappedSuperclass 사용법 (0) | 2023.11.23 |
JPA 프록시와 지연로딩/즉시로딩 (1) | 2023.11.23 |
JPA의 Entity는 기본 생성자가 왜 반드시 필요할까? (1) | 2023.11.20 |
EntityManager와 EntityManagerFactory (0) | 2023.11.20 |
들어가면서
JPA를 사용해서 개인프로젝트를 진행하면서 발생한 N+1문제와 이를 해결하면서 겪은 오류에 대해 정리해보려고한다. 간단한 오류해결 문제만 정리를 하려고 하니 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 조인을 사용하게 되면 해당 부분에 오류가 발생하기 때문에 fetch join 이나 복잡한 쿼리의 경우 countQuery를 분리해서 사용해줘야한다고한다.
@Query(value = "select p from PostEntity p join fetch p.user",
countQuery = "SELECT COUNT(DISTINCT p) FROM PostEntity p INNER JOIN p.user")
Page<PostEntity> findAllJoinFetch(Pageable pageable);
참고문서
'spring 🍀 > spring-data-jpa' 카테고리의 다른 글
Spring Data JPA Auditing으로 엔티티의 생성/수정 시각 자동으로 기록 + 생성자/수정자도 기록하기 (1) | 2023.11.23 |
---|---|
@MappedSuperclass 사용법 (0) | 2023.11.23 |
JPA 프록시와 지연로딩/즉시로딩 (1) | 2023.11.23 |
JPA의 Entity는 기본 생성자가 왜 반드시 필요할까? (1) | 2023.11.20 |
EntityManager와 EntityManagerFactory (0) | 2023.11.20 |