@EntityGraph
연관된 엔티티들을 SQL한번에 조회하는 방법
현재 Member와 Team엔티티는 다대일 연관관계를 가지고 있는데, fetch=FetchType.LAZY로 되어 있어 지연 로딩 전략을 하고있다.
Member엔티티
@Entity
public class Member {
@Id @GeneratedValue
@Column(name="member_id")
private Long id;
private String username;
private int age;
@ToString.Exclude//연관관계 필드는 toString을 안하는 것이 좋다.
@JoinColumn(name="team_id")
@ManyToOne(fetch=FetchType.LAZY)
private Team team; /* Team과 다대일 연관관계 */
}
하여 JPA로 Member를 조회하면 Team은 프록시 객체로 가져온 뒤, Team관련 데이터를 조회할 때 쿼리가 나가는 N+1문제가 발생한다.
( 이해가 안간다면 페치 조인 포스트 참고 )
아래와 같이 이를 테스트해보자.
@Test
void findMemberLazy(){
//given
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
teamJPARepository.save(teamA);
teamJPARepository.save(teamB);
Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 10, teamB);
memberRepository.save(member1);
memberRepository.save(member2);
em.flush();
em.clear();
//when
List<Member> members = memberRepository.findAll();
for (Member member : members){
System.out.println("member = " + member.getUsername());
/* 여기서 team 쿼리가 나간다! */
System.out.println("member.team = " + member.getTeam().getName());
}
}
이를 실행하면 한번에 Team까지 join하지 않고 아래와 같이 각 member1, member2의 Team의 name정보를 얻기 위해 주석 아래의 코드에서 team의 name을 select하기 위한 쿼리가 나가게 되는 것이다. 이와 같은 일은 현재 Member가 Team을 Lazy로 설정했기 때문이다.
select
member0_.member_id as member_i1_0_,
member0_.age as age2_0_,
member0_.team_id as team_id4_0_,
member0_.username as username3_0_
from
member member0_
// member1의 team.name 조회 쿼리
select
team0_.team_id as team_id1_1_0_,
team0_.name as name2_1_0_
from
team team0_
where
team0_.team_id=?
// member2의 team.name 조회 쿼리
select
team0_.team_id as team_id1_1_0_,
team0_.name as name2_1_0_
from
team team0_
where
team0_.team_id=?
위와 같이 세번의 쿼리가 나간다. 하여 이를 순수 JPA를 사용할때는 페치 조인을 사용하여 해결했다.
data jpa 리퍼지토리에서는 아래와 같이 할 수 있을 것이다.
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();
}
하지만 이렇게 일일히 fetch join 쿼리를 작성하는 것은 꽤나 귀찮다. 하여 훨씬 쉬운 방법을 제공하는데, 그것이 @EntityGraph이다.
@EntityGraph
아래와 같이 하면 된다.
public interface MemberRepository extends JpaRepository<Member, Long> {
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
}
위처럼 @EntityGraph(attributePaths ={})의 대괄호 안에 fetch join하고 싶은 엔티티를 넣어준다. 현재 Memer클래스의 필드에 Team team이라 되어 있으므로, "team"을 적어준 것이다. 이를 테스트 해보자.
@Test
void findMemberEntityGraph(){
//given
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
teamJPARepository.save(teamA);
teamJPARepository.save(teamB);
Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 10, teamB);
memberRepository.save(member1);
memberRepository.save(member2);
em.flush();
em.clear();
//when
List<Member> members = memberRepository.findAll();
for (Member member : members){
System.out.println("member = " + member.getUsername());
System.out.println("member.team = " + member.getTeam().getName());
}
}
실행결과 아래처럼 fetch join이 적용된 하나의 쿼리만 나갔다.
select
member0_.member_id as member_i1_0_0_,
team1_.team_id as team_id1_1_1_,
member0_.age as age2_0_0_,
member0_.team_id as team_id4_0_0_,
member0_.username as username3_0_0_,
team1_.name as name2_1_1_
from
member member0_
left outer join
team team1_
on member0_.team_id=team1_.team_id
또한 아래와 같이 Member를 select하는 쿼리만 짠 뒤, @EntityGraph로 페치 조인만 추가하는 것도 가능하다.
public interface MemberRepository extends JpaRepository<Member, Long> {
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findAll();
}
또한 아래와 같이 메서드 이름으로 쿼리를 정의하는 방법으로도 사용가능하다.
public interface MemberRepository extends JpaRepository<Member, Long> {
@EntityGraph(attributePaths = {"team"})
List<Member> findEntityGraphByUsername(@Param("username") String username);
//find ... by 의 ...에는 아무거나 적어도 된다.
}
김영한님의 인프런 강의와 PDF를 바탕으로 정리하였습니다.
'JPA > 스프링 DATA JPA' 카테고리의 다른 글
Data JPA 10 사용자 정의 리포지토리 구현 (0) | 2022.01.08 |
---|---|
Data JPA 09 JPA Hint (0) | 2022.01.08 |
Data JPA 07 벌크성 수정 쿼리 (0) | 2022.01.07 |
Data JPA 06 쿼리 메서드 - 반환타입, 페이징 (0) | 2022.01.06 |
Data JPA 05 쿼리 메서드 - Query, 리포지토리 메서드에 쿼리 정의하기 (0) | 2022.01.05 |