Skip to content

CheorHyeon/JPANplusOneProblemSolveExam

Repository files navigation

JPA N+1 문제 발생 및 해결 Repository

  • 어느날 N+1문제를 DTO로 해결했다는 말을 들었다는 이야기를 들은 경험이 있다.
    • 음.. @BatchSize 나 fetch join은 아는데.. DTO 자체로도 해결이 된다고? 알지 못하는 뭔가가 더 있나? 하는 궁금증이 들었다.
    • 결론부터 말하자면 DTO로 해결했다고 하는 것은 상당히 부족한 답변이 아닐까 싶다.
      • 왜 이렇게 생각했는지는 블로그에서 확인할 수 있다!

상세 구현 과정 설명은 블로그 글 참조

프로젝트 세팅

버전 및 의존성

  • SpringBoot 3.3.4 / java 17 /
  • Lombok, SpringWeb, JPA, H2 DB, Querydsl
  • DB 생성하지 않고도 발생하는 쿼리를 보기 위해 H2 DB in memory 모드를 사용

실행해보기

  • 해당 Repo full 받기
  • 각 TestCase 실행하며 쿼리 확인하기

초기 데이터 세팅

각 Case별 실험

해결 Case 0 : Join하면 연관 관계에 있는 Entity를 넣어주지 않을까?

해결 Case 1 : FetchJoin을 통해 조회 대상이 되는 엔티티를 가져올 때 연관된 엔티티까지 가져오자(ManyToOne)

해결 Case 2 : FetchJoin에서 Paging을 할 때 ManyToOne 관계라면 offset과 limit이 정상 동작한다

해결 Case 3 : FetchJoin에서 Paging을 할 때 OneToMany 관계라면 offset과 limit이 쿼리에서 작동하지 않고, 전체 데이터를 불러와 메모리에서 페이징 처리를 해준다.

해결 Case 4 : @BatchSize를 이용하여 여러 select 쿼리들을 지정된 갯수만큼 In Query로 모아 N+1문제 해결

해결 Case 5 : select 메서드에 필요한 속성을 직접 기입하여 DTO로 받기

해결 Case 6 : select 메서드에 필요한 속성을 직접 기입하여 Tuple로 받기

그래서 결론은?

  • Proxy객체의 메서드를 호출하면 실제 객체가 필요하기 때문에 N개의 추가 쿼리가 발생한다.
  • 하지만 쿼리를 날릴 때 애초에 필요한 속성들을 가져온다던가, 아니면 fetchJoin으로 한방에 가져온다던가 하면 추가 쿼리는 발생하지 않는다.
    • 단 OneToMany 관계일때는 Paging을 사용할 때 fetchJoin 사용 지양(메모리 과부하 문제 가능)

연관관계에 있는 Entity의 특정 속성을 사용하여 JPQL 쿼리를 생성하면 속성값만 가져오기 때문에 프록시 객체를 굳이 실제 Entity 객체로 불러오지 않는다.

  • Product의 Category Name이 필요하다면 select 절에 product.category.name 이런식으로 특정 컬럼을 지정
  • 그러면 나중에 DTO라던가 Tuple을 활용할 때 연관관계에 있는 Category 객체가 필요한 것이 아니니 Proxy 객체를 실제 객체로 바꾸지 않고 name 값만 사용

그렇다면, 맨 위에서 말한 DTO를 활용하는 것이 N개의 쿼리를 무조건 발생 안할까?

  • 여태 위에서 Proxy 객체의 메서드를 호출하면 실제 객체가 필요하니 추가 쿼리가 필요함을 알았다.
  • 그래서 프록시 객체로 필요한 속성 값을 가져오는 것이 아닌 DTO나 Tuple을 활용해서 N개의 쿼리가 추가로 발생하는 것을 회피하는 방식을 바로 직전에 알아봤다.

About

JPA N+1 문제 여러 방법 해결 레포지토리

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages