JPA를 공부하면서 자주 등장하던 N+1 문제라는 것에 대해서 정리해보자 !
결과적으로 쿼리가 1 + N번 실행되므로 N+1 문제라고 부른다 !
Team과 Member가 1:N 관계로 매핑되어 있다고 가정했을 때
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
}
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
}
팀과 그 팀에 소속된 멤버를 출력하는 코드를 이렇게 작성할 수 있다.
public List<Team> findAllTeams() {
List<Team> teams = teamRepository.findAll(); // (1) Team을 조회하는 쿼리 실행
for (Team team : teams) {
team.getMembers().size(); // (2) 각 Team의 Member를 조회하는 쿼리 실행
}
return teams;
}
결과적으로 팀이 10개라면 총 11번의 쿼리가 실행된다.
@Query("SELECT t FROM Team t JOIN FETCH t.members")
List<Team> findAllTeamsWithMembers();
이 방식은 조인을 통해 Team과 Member를 한 번의 쿼리로 가져오므로 성능 저하를 방지할 수 있다.
SELECT t.*, m.*
FROM team t
JOIN member m ON t.id = m.team_id;
@EntityGraph(attributePaths = "members")
@Query("SELECT t FROM Team t")
List<Team> findAllTeamsWithMembers();
@OneToMany(mappedBy = "team")
@BatchSize(size = 10)
private List<Member> members;
이 방식은 각 팀의 멤버를 한 번에 묶어서 조회하므로 쿼리 수를 줄이는 데 효과적이다.