빡코

[스프링 데이터 JPA] fetchJoin과 EntityGrap 본문

Java/JPA

[스프링 데이터 JPA] fetchJoin과 EntityGrap

chris.djang 2023. 5. 11. 15:15

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/

 

Spring Data JPA - Reference Documentation

Example 119. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del

docs.spring.io

Member와 Team은 N:1 지연로딩 관계이다. Team의 데이터를 조회할 때 마다 N+1의 쿼리 성능 문제가 발생한다. 

public class Member {

    @Id @GeneratedValue
    @Column(name="member_id")
    private Long id;
    private String username;
    private int age;

 	//가짜 객체를 먼저 가져온다.
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;
}
@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", 20, 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 = " + member.getTeam().getName()); ---> 여기서 문제가 발생 
    }

}

 

JPQL 페치조인(실무사용)

Fetch 조인으로 연관 데이터 한번에 조회하기. 빈 proxy가 아닌 실제 조회된 데이터로 entityr가 채워진다.  

public interface MemberRepository extends JpaRepository<Member, Long> {
    @Query("select m from Member m left join fetch m.team") //member와 연관된 team을 한번에 끍어 온다.
    List<Member> findMemberFetchJoin();
}

//실행 쿼리
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

 

EntityGraph

-fetch 조인을 편리하게 할 수 있음. 

//공통메서드오버라이드
    @Override
    @EntityGraph(attributePaths ={"team"})
    List<Member>findAll();

    @EntityGraph(attributePaths = {"team"})
    @Query("select m from Member m")
    List<Member> findMemberEntityGraph();

    @EntityGraph(attributePaths = {"team"})
    List<Member> findByUserName(@Param("username") String username);

 

NamedEntityGraph 사용방법

//Member
@NamedEntityGraph(name = "Member.all", attributeNodes = @NamedAttributeNode("team"))
public class Member {
}


//스프링 데이터 JPA 레포지토리 
public interface MemberRepository extends JpaRepository<Member, Long> {

    //@EntityGraph(attributePaths = {"team"})
    @EntityGraph("Member.all")
    List<Member> findEntityGraphByUsername(@Param("username") String username);


}