N+1 문제???
스프링에서 객체 간 연관 관계 설정시에 발생하는 문제로 조회한 데이터만큼의 SQL 쿼리가 추가적으로 발생해 읽어오는 문제를 말합니다. Fetch 전략을 Lazy나 Eager 어떤 전략과 상관없이 N+1 문제는 발생하게 됩니다.
원인
스프링에서 SQL 조회 쿼리를 날려주는 JPQL(Java Persistence Query Language)를 실행하게 되면 JPA가 분석해 쿼리를 작성하게 됩니다. 그러나 JPQL은 Fetch 전략에 대한 정보가 없기 때문에 개발자가 선택한 특성을 무시하고 쿼리를 작성하게 됩니다.
해결 방법
Fetch Join
사용법
@Query("select a from Account a join fetch a.product")
List<Account> findAllByProducts();
실제 쿼리
select a
from account a
left join fetch a.product
계정 엔티티인 account와 해당 계정이 올린 상품인 Product 엔티티가 있을때 account와 product 관계는 일대다 관계입니다. 일대다 관계는 기본 설정이 Lazy로 설정되어 있어 기본 조회시 조회가 되지 않습니다. 그러므로 Fethch join으로 N+1문제가 발생하지 않도록 데이터를 한 방에 가져오게 해야 합니다.
@EntityGraph
Fetch join을 하기 위해 매번 JPQL문을 작성할 필요는 없다. 스프링 DATA JPA에서 @EntityGraph 기능으로 단순히 클래스를 적어놓는 것만으로 Fetch Join의 불편함을 해결했습니다.
@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
@EntityGraph(attributePaths = "product")
Optional<Account> findAccountWithProductByAccountId(Long accountId);
}
Fetch Join, Entity Graph 사용시 조심해야할 점
기본적으로 Join을 사용해서 카르테시안 곱이 발생할 수 있어 중복 호출되는 데이터가 존재할 수 있다. 위 예시에서 product를 List로 뒀다면 자료구조를 Set으로 바꿔 중복 데이터를 제거할 수 있을 것이다.
프로젝트 적용
기존 코드에선 Account와 Account와 일대다 관계인 Product 또한 Product와 일대다 관계인 urlList까지 조회를 했어야 하는 상황이었다. 초기버전에서 엔티티 그래프를 적용하지 않았을때 account 한개를 불러올때 관계된 product,product와 관계된 urlList까지 불필요한 SQL문들이 중복되어 만들어지는 것을 확인했었다. 다중 엔티티 그래프 적용을 위해 Account 엔티티에 아래 내용을 기재해두고 필요한 메서드에서만 사용할 수 있게 해두었다.
@NamedEntityGraph(
name = "Account.withProductAndUrl",
attributeNodes = {
@NamedAttributeNode(value = "products", subgraph = "urlList")
},
subgraphs = @NamedSubgraph(name = "urlList", attributeNodes = @NamedAttributeNode("urlList"))
)
@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
@EntityGraph("Account.withProductAndUrl")
Optional<Account> findAccountWithProductByAccountId(Long accountId);
}
'스프링 부트 > JPA' 카테고리의 다른 글
[스프링] DATA JPA In절로 파라미터 넣기 (0) | 2022.08.25 |
---|---|
[스프링] @Query에 ENUM 타입 쓰는 법 (0) | 2022.08.25 |
@NotNull, @NotEmpty, @NotBlank 차이 (0) | 2022.02.19 |
스프링 부분 수정 쿼리 @DynamicUpdate (1) | 2022.02.18 |
스프링 부트 JPA 기본 정리 (0) | 2022.01.19 |
댓글