✅ @EntityGraph ✅ @NamedEntityGraph
지연로딩에서의 N+1 문제
member team은 지연로딩 관계이다.
member를 find하면 team은 가짜, 프록시로 가져와 진다.
따라서 team의 실제 데이터가 필요할 때는 조회할 때 마다(team.getName) 쿼리가 실행된다.
즉, team의 실제 데이터가 필요할 때마다 네트워크로 쿼리를 날리기 때문에 비효율적이다.
이것을 N+1 문제라고 하고
쿼리 하나로 member와 관련된 team에 대한 정보도 프록시가 아닌 실제 객체로 모두 가져오는 방법으로 N+1 문제를 해결할 수 있다.
아래처럼 JPQL로 패치 조인을 만들어 문제를 해결할 수는 있지만, 경우마다 쿼리문을 작성해야 하는 번거로움이 있다.
@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();
- 간단한 경우 : @EntityGraph로 간단하게 패치조인을 구현
- 복잡한 경우 : JPQL로 직접 패치조인을 구현
연관된 엔티티들을 SQL 한번에 조회
public interface MemberRepository extends JpaRepository<Member, Long> {
// @EntityGraph
// 1. 공통 메서드 오버라이드
@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
// 2. JPQL + 엔티티 그래프
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
// 3. 메서드 이름으로 쿼리에서 특히 편리하다.
@EntityGraph(attributePaths = {"team"})
List<Member> findEntityGraphByUsername(String username);
}
@NamedQuery처럼 엔티티에 선언하여 사용하는 방법
(강사님은 잘 사용하지 않는다고 함, 아마 엔티티가 더러워져서 인듯)
@NamedEntityGraph(name = "Member.all", attributeNodes =
@NamedAttributeNode("team"))
@Entity
public class Member {
...
}
public interface MemberRepository extends JpaRepository<Member, Long> {
@EntityGraph("Member.all")
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
}