개요
- @EntityGraph 를 통해 JPQL 을 사용하지 않고 FETCH JOIN 을 간단하게 사용할 수 있다.
- 간단한 연관 관계에서의 FETCH JOIN 은 @EntityGraph 를 사용하고, 복잡한 관계는 JQPL 로 직접 풀어내면 좋을 것 같다.
- EntityGraph 는 LEFT OUTER JOIN 을 사용한다.
N + 1 문제
@Test
void findMemberLazy() {
//given
//member1 -> teamA
//member2 -> teamB
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
teamRepository.save(teamA);
teamRepository.save(teamB);
Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 10, teamB);
memberRepository.save(member1);
memberRepository.save(member2);
entityManager.flush();
entityManager.clear();
//when
//select Member
List<Member> members = memberRepository.findAll();
for (Member member : members) {
System.out.println("member = " + member);
System.out.println("team class = " + member.getTeam().getClass()); //proxy
System.out.println("team name = " + member.getTeam().getName()); //select Team
}
}
- Member Entity 와 Team Entity 는 N:1 연관 관계를 가진다.
- 그리고 Member Entity 에서 Team 은 지연 로딩으로 설정되어 있다.
- Member Entity 조회 시 Team 은 Proxy 객체로 가져온다.
- 그리고 조회한 Member Entity 에서 Team 의 Field 를 사용 시 Team 을 조회한다.
- member.getTeam().getName()
- N + 1 문제가 발생한다.
- 하나(1)의 SELECT Member 쿼리를 날려서 2개의 결과가 나왔다.
- 그러면 각각의 결과에 대한 Team 을 두 번(N) 더 조회한다.
- 만약 Member 가 10개의 결과가 나왔다면 Team 사용 시 10개의 팀 쿼리가 더 나갈 것이다.
- 이 문제는 FETCH JOIN, @EntityGraph, BatchSize 를 통해 해결할 수 있다.
JPQL 페치 조인
@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();
- 스프링 데이터 JPA는 JPA가 제공하는 엔티티 그래프 기능을 편리하게 사용하게 도와준다.
- 이 기능을 사용하면 JPQL 없이 페치 조인을 사용할 수 있다. (JPQL + 엔티티 그래프도 가능)
EntityGraph
// 공통 메서드 오버라이드
@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
// JPQL + 엔티티 그래프
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
// 메서드 이름으로 쿼리에서 특히 편리하다.
@EntityGraph(attributePaths = {"team"})
List<Member> findByUsername(String username)
- List<Member> findAll();
- 상속 관계에 있는
ListCrudRepository Interface 에 있기 때문에 Override 로 가져올 수 있다.