본문 바로가기

JPA

[JPA] N+1문제

N+1 문제란 ? 

요청이 1개의 쿼리로 처리 되길 기대했지만, N개의 추가 쿼리(하위 엔티티들 조회 쿼리)가 발생하는 현상

예시

JPA에서 "팀(1개) - 팀원(5명)" 과 같은 1대 N의 연관관계가 Lazy Fectch로 존재할 경우, 팀의 팀원을 조회하면 총 6번의 쿼리가 실행된다. 먼저, 처음 팀을 조회하는 쿼리가 발생하고, 해당 팀의 팀원들에 대한 쿼리가 5개 실행되어 총 6번의 쿼리분이 발생하기 때문이다.

원인

JPA에서 FetchType을 Lazy Fetch Loading으로 설정할 경우, 처음 팀을 조회 시 팀원 데이터를 함께 가져오지 않고, 팀원에 대한 Proxy 객체만 가져온다. 이후, 조회한 팀에서 팀원을 조회할 때, JPA의 1차 캐시에서 팀원에 대한 데이터가 있는지 확인 후 쿼리를 실행하여 각각의 팀원 객체에 대한 데이터를 가져온다.

 

요약하면, Lazy Fetch 때문에, 팀을 조회할 때, 팀원 데이터가 누락되어 각각의 팀원에 대한 쿼리를 새로 실행하는 것이다!

🔥 Eager Loding 으로 바꿔도 N+1 문제는 발생한다.
JPA는 조회 대상을 기준으로 쿼리문을 날리기 때문에 Eager Loding으로 바꿔도, 처음에 팀을 조회 후, 곧 바로 팀원에 대한 N개의 쿼리가 실행된다.

해결 방법

  • join fetch
    • Inner Join
    • 하위 엔티티의 수만큼 중복 발생 (해당 1:다 필드에서 List가 아닌 Set)
@Query("select e from 엔티티 e join fetch e.하위엔티티")
List findAll();

 

  • @EntityGraph
    • Outer Join
    • 하위 엔티티의 수만큼 중복 발생
@EntityGraph(attributePaths = "하위 엔티티")
@Query("select e from 엔티티 e"0
List findAll();

 

위의 join fetch와 entityGrapgh를 이용할 경우, 하위 엔티티의 수만큼 중복이 발생한다.

해당 중복을 제거하는 방법은 아래와 같이 2가지가 있다.

  1. Set 자료형
    • 1:다 필드에서 List형이 아닌, Set 자료형을 사용해 중복을 제거.
  2. distinct 
    • join fetch 쿼리 애너테이션을 추가할때, "select Distinct ... " 와 같이 DISTINCT를 추가한다. 

참고 출처

https://www.youtube.com/watch?v=ni92wUkAmQI   

https://jojoldu.tistory.com/165 ( 꼭 한번 읽어보세요 )

'JPA' 카테고리의 다른 글

JPA - 연관관계 주인(feat. mappedBy)  (0) 2023.05.19
영속성 컨텍스트!  (2) 2023.05.11
자바의 트랜잭션  (0) 2023.05.09
엔티티 매니저와 영속성 컨텍스트 정리  (0) 2023.05.01
1. JPA의 등장  (0) 2023.04.26