Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

@EntityGraph 본문

인프런/[인프런] Spring Data JPA

@EntityGraph

Seung__ 2021. 8. 29. 22:51

1. @EntityGraph?


  • 연관된 엔티티들을 SQL 한번에 조회하는방법

 

 

2. 사례


  • member -> team은 다대일 지연로딩 관계.
  • 따라서 team에 대한 데이터 접근(team.get~) 시 쿼리가 날라감.
  • 따라서 member에 대한 쿼리 외에 team의 데이터 조회하는 쿼리 나감.
  • 즉 N+1 이슈 발생함.

 

 

 

3. member만 조회


@Test
    public 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);

        em.flush();
        em.clear();

        //when
        List<Member> members = memberRepository.findAll();

        System.out.println("===================");
        for (Member member : members) {
            System.out.println("member.getUserName() = " + member.getUserName());
        }
    }

멤버 조회하는 쿼리만 날라감.

    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.user_name as user_nam3_0_ 
    from
        member member0_

===================
member.getUserName() = member1
member.getUserName() = member2

 

 

 

4. member의 팀이름 조회


  • member.getTeam().getName()
  • 지연로딩 이므로 team.getName()실행 시 프록시 초기화됨.
  • 이 때  team을 조회하는 쿼리 날라감.
  • N+1!!!
@Test
    public 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);

        em.flush();
        em.clear();

        //when
        List<Member> members = memberRepository.findAll();

        System.out.println("===================");
        for (Member member : members) {
            System.out.println("member.getUserName() = " + member.getUserName());
            System.out.println("member.getTeam().getName() = " + member.getTeam().getName());
        }
    }
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.user_name as user_nam3_0_ 
    from
        member member0_

===================
member.getUserName() = member1
                    
    select
        team0_.team_id as team_id1_1_0_,
        team0_.name as name2_1_0_ 
    from
        team team0_ 
    where
        team0_.team_id=?


member.getUserName() = member2
 
    select
        team0_.team_id as team_id1_1_0_,
        team0_.name as name2_1_0_ 
    from
        team team0_ 
    where
        team0_.team_id=?

 

 

 

 

5. fetch join 사용


@Query("select m from Member m join fetch m.team t")
List<Member> findMemberFetchJoin();
   @Test
    public 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);

        em.flush();
        em.clear();

        //when
        List<Member> members = memberRepository.findMemberFetchJoin();

        System.out.println("===================");
        for (Member member : members) {
            System.out.println("member.getUserName() = " + member.getUserName());
            System.out.println("member.getTeam().getName() = " + member.getTeam().getName());
            System.out.println("member.getTeam().getClass() = " + member.getTeam().getClass());
        }
    }
  • fetch join을 사용하면 member조회시 연관엔티티인 team도 모두 조회하여
  • team엔티티 생성해서 Team에 넣어둠. 프록시 객체가 아님.
    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_.user_name as user_nam3_0_0_,
        team1_.name as name2_1_1_ 
    from
        member member0_ 
    inner join
        team team1_ 
            on member0_.team_id=team1_.team_id

===================
member.getUserName() = member1
member.getTeam().getName() = teamA
member.getTeam().getClass() = class study.datajpa.entity.Team
member.getUserName() = member2
member.getTeam().getName() = teamB
member.getTeam().getClass() = class study.datajpa.entity.Team

 

 

 

6. 문제점, 방법


  • 근데 SpringDataJpa는 매번 @Query를 사용하는게 아닌데:?
  • 그럼 fetch join  어떻게 적용하지?
  • @EntityGraph -> fetch join 적용 가능. 어떤 엔티티에 적용할지 정해줘야함.
    • attributePath = {""} -> You can refer to direct properties of the entity or nested properties via a property.nestedProperty.
    • 기본적으로 left outer join임
@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
   @Test
    public 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);

        em.flush();
        em.clear();

        //when
        List<Member> members = memberRepository.findAll();

        System.out.println("===================");
        for (Member member : members) {
            System.out.println("member.getUserName() = " + member.getUserName());
            System.out.println("member.getTeam().getName() = " + member.getTeam().getName());
            System.out.println("member.getTeam().getClass() = " + member.getTeam().getClass());
        }
    }
    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_.user_name as user_nam3_0_0_,
        team1_.name as name2_1_1_ 
    from
        member member0_ 
    left outer join
        team team1_ 
            on member0_.team_id=team1_.team_id

===================
member.getUserName() = member1
member.getTeam().getName() = teamA
member.getTeam().getClass() = class study.datajpa.entity.Team
member.getUserName() = member2
member.getTeam().getName() = teamB
member.getTeam().getClass() = class study.datajpa.entity.Team
  • @Query, @EntityGraph같이 사용 가능
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();

 

 

8. NamedGraph


  • Member Class에 @NamedEntityGraph 사용 가능
  • @NamedAttributeNode(("엔티티 이름"))
@NamedEntityGraph(name = "Member.all", attributeNodes =
@NamedAttributeNode("team"))
@Entity
public class Member {}
  • 사용방법
  • @EntityGraph("name")
@EntityGraph("Member.all")
@Query("select m from Member m")
List<Member> findMemberEntityGraph();

 

 

8. GitHub : 210829 @EntityGraph


 

GitHub - bsh6463/SpringDataJpa

Contribute to bsh6463/SpringDataJpa development by creating an account on GitHub.

github.com

 

'인프런 > [인프런] Spring Data JPA' 카테고리의 다른 글

사용자 정의 Repository  (0) 2021.08.30
JPA Hint & Lock  (0) 2021.08.29
Query Method, 벌크성 수정 쿼리  (0) 2021.08.29
SpringDataJpa 페이징, 정렬  (0) 2021.08.29
JPA의 페이징과 정렬  (0) 2021.08.29
Comments