Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JPA] N+1 문제 해결 #6

Open
youngpark17 opened this issue Oct 1, 2021 · 0 comments
Open

[JPA] N+1 문제 해결 #6

youngpark17 opened this issue Oct 1, 2021 · 0 comments

Comments

@youngpark17
Copy link

youngpark17 commented Oct 1, 2021

문제발생

JPA @OneToMany, @ManyToOne 사용시 N+1 문제

ex) user와 task가 1:N일때 user조회 후 task 조회시 fetch type 상관없이 user수 만큼 select 쿼리 추가 발생

원인

jpaRepository에 정의한 인터페이스 메서드를 실행하면 JPA는 메서드 이름을 분석해서 JPQL을 생성하여 실행하게 된다. JPQL은 SQL을 추상화한 객체지향 쿼리 언어로서 특정 SQL에 종속되지 않고 엔티티 객체와 필드 이름을 가지고 쿼리를 한다. 그렇기 때문에 JPQL은 findAll()이란 메소드를 수행하였을 때 해당 엔티티를 조회하는 select * from Owner 쿼리만 실행하게 되는것이다. JPQL 입장에서는 연관관계 데이터를 무시하고 해당 엔티티 기준으로 쿼리를 조회하기 때문이다. 그렇기 때문에 연관된 엔티티 데이터가 필요한 경우, FetchType으로 지정한 시점에 조회를 별도로 호출하게 된다.

해결방법

  1. join fetch
  2. Query Dsl fetch join
  3. @BachSize(size=N): 연관객체를 조회할때 지정한 사이즈(N)만큼 In 절 이용
  4. @entitygraph

심화

  • Fetch Join과 EntityGraph는 JPQL을 사용하여 JOIN문을 호출한다는 공통점이 있다. 또한, 공통적으로 카테시안 곱(Cartesian Product)이 발생하여 Owner의 수만큼 Cat이 중복 데이터가 존재할 수 있다. 그러므로 중복된 데이터가 컬렉션에 존재하지 않도록 주의해야 한다.
@Entity
class Team{
    long id;
    List<Member> memberList;
}

@Entity
class Member{
    long id;
    String name;
}

즉 이런식의 엔티티 구조일때 teamRepository.findAll()을 통해 조회해온다면 위와 같이 조회가 되고 사용하게 되는 Team의 경우는 하나지만, 실제로는 위 표와 같이 가져와 지기 때문에 다른 memberList를 들고있는 같은 id의 Team이 두개 생성된다.
즉,

List<Team> teamList = repo.findAll()

teamList.size()의 결과는 1이고, 해당 team의 memberList의 사이즈는 2가 되어야 하지만, teamList.size()의 결과가 2가 되는 경우가 발생한다.

이를 막기 위해 일반적으로 distinct 또는 Set 자료구조를 사용해야 한다. 여기서의 distinct란 sql의 distinct가 아닌 JPQL의 distinct로써 위 예제와 같이 동일한 키를 가지는 team을 제거한다.

이러한 문제는

-> @OneToMany 대신 @ManyToOne 을 사용할 경우 중복이 일어나지 않는다. 또한 jpa api를 통한 페이지 네이션또한 가능하다.(@OneToMany일떄는 불가) "@OneToMay" 지양하자.(잘생각해보고 꼭 필요한 경우만)

참고링크1
참고링크2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant