EntityGraph

 

 

지연로딩 간단 개념

엔티티를 설계할 때 연관관계 매핑에서 FetchType을 보통 LAZY로 설정해주는 게 일반적이다.

 

지연로딩이라고 흔히 부르는 이 설정은 다양한 연관관계로 매핑되어 있는 엔티티를 호출할 때

셀렉트 쿼리의 최적화를 위해 다른 엔티티를 프록시 객체로 생성하는 것을 말한다.

 

ex) Member와 Team이 N:1(다대일) 관계일 때 Member 엔티티를 호출하면 Team 엔티티는 프록시 객체로 생성되고 Member에만 select 쿼리를 날린다.

 

쿼리 최적화를 위해 지연로딩 설정을 해주지만 오히려 이런 설정이 최적화에 불리할 때가 있다.

 

Member(N) : Team(1) 엔티티 관계를 예로 들겠다.

 

Member를 생성하면 Team은 프록시 객체다.

 

그렇기에 Team 엔티티가 필요해지면, Team을 위한 별도의 select 쿼리가 추가로 실행된다.

 

각각 다른 Member 10개와 그 안에 매핑된 각각의 Team이 1개씩 총 10개가 필요하다면, 쿼리 실행 횟수는 최소 20(Member 개수 + Team 개수)이 될 것이다.

 

그 외의 엔티티가 더 복잡하게 엮여있다면 그 회수는 기하급수적으로 증가할 수 있다.

 

그런데 애초에 Member 엔티티에 select 쿼리를 보낼 때 Team 까지 한번에 조회한다면 쿼리 실행 횟수는 Member의 개수인 N번으로 줄어든다.

 

그것을 도와주는게 EntityGraph다.

 

EntityGraph는 엔티티를 조회할 때 페치 조인(Fetch Join)으로 한번에 모든 엔티티를 가져오게 도와준다.

 

 

EntityGraph 사용 방법

@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();

레파지토리에 구현된 메서드 중 원하는 것에 @EntityGraph 어노테이션을 추가한다.

 

그리고 attributePaths로 Member와 매핑된 Team 객체의 변수 명을 입력해준다.

 

현재 예시에서 Member 엔티티는 아래 처럼 구현된 상태이니 참고하면 된다.

 

@Entity
@Getter
@Setter
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "team_id")
    private Team team;

 

이렇게 하고 위에서 구현한 메서드를 호출하면 Member와 Team 객체를 한꺼번에 select 쿼리로 가져오게 된다.

 

본래 LAZY 설정에 따라 Member 객체에만 select 쿼리를 실행해야 하지만 EntityGraph 설정 덕에 가능한 것이다.

 

@NamedEntityGraph

약간 다른 방식으로도 가능하다.

@Entity
@Getter
@Setter
@NamedEntityGraph(name = "Member.all", attributeNodes = @NamedAttributeNode("team"))
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "team_id")
    private Team team;

엔티티에 @NamedEntityGraph 어노테이션을 추가하고 name과 attributeNodes를 작성한다.

 

attributeNodes는 처음 방법에서 레파지토리에 작성한 attributePaths 대신 넣는다고 보면 된다.

 

    @EntityGraph("Member.all")
    List<Member> findAll();

그리고 레파지토리로 와서 구현한 메서드에 @EntityGraph를 붙이고 바로 위에서 작성한 name을 넣으면 된다.

 

 

@EntityGraph 없이 해결

EntityGraph는 과정을 편하게 만들어주는 것이며, 순수 JPQL을 작성해 해결할 수도 있다.

    @Query("select m from Member m join fetch m.team")
    List<Member> findMemberFetchJoin();

페치 조인(Fetch Join)을 사용하면 left join을 통해 Team 엔티티의 값도 가져오게 된다.

 

EntityGraph를 사용해 예를 든 방법과 결과물은 같다.

 

복잡한 쿼리가 아니라면 순수 JPQL로 작성하는 것도 고려해볼만 한다.

 

복사했습니다!