티스토리 뷰

dev_공부일지/spring data JPA

@EntityGraph

dev_0hoon 2024. 3. 6. 15:39

참고로 이 기능은 순수 JPA 2.2 부터 들어간 기능이라 한다.

@Test
    public void findMemberLazy(){
        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();

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

        for (Member member : members) {
            System.out.println("member.getUsername = " + member.getUsername());
            System.out.println("member.getTeam = " + member.getTeam());
            System.out.println("member.getTeam().getName" + member.getTeam().getName());
        }
    }​
package study.datajpa.entity;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED) // 기본생성자 만들기, 기본생성자는 JPA 스펙으로 private는 안된다.
@ToString(of = {"id", "username", "age"}) //toString을 만들 때에는 가급적이면 연관관계객체는 넣지 않는것이 좋다(서로 부르면서 무한루프 된다.)
@NamedQuery(
        name="Member.findByUsername",
        query="select m from Member m where m.username =: username"
)
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;

    public Member(String username) { this.username = username; }

    public Member(String username, int age) {
        this.username = username;
        this.age = age;
    }
    public Member(String username, int age, Team team) {
        this.username = username;
        this.age = age;

        if(team != null) {
            changeTeam(team);
        }
    }

    public void changeTeam(Team team){
        this.team = team;
        team.getMembers().add(this);
    }
}

 

위의 코드에서 Member 객체 안에 Team은 현재 FetchType.LAZY로 설정 되어있다. member에 대한 findAll을 했을 때에는 team에 대한 쿼리가 날아가지 않는다.

 

후에 team을 호출하는 for문이 돌아 갈때 한개씩 조인하여 가져온다.

 

이 문제는 1 + N의 문제를 유발하는데 데이터가 많아질 경우 트래픽을 유발 할 수 있다는 문제점이 있다.

문제점을 바로 잡기 위해 보통은 쿼리에 join fetch를 넣어 해결한다.

 

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

 

data JPA에서는 위와 같은 방법을 사용할 수도 있지만 좀더 편리한 방법이 있다. 매번 join fetch를 넣어 해결하는 방식은 시간을 잡을 수 있어서 인지 줄여버렸다.

 

@EntityGraph를 사용하는 방식이다.

 

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

 

이렇게 객체 그래프를 엮어서 가져올 수가 있다

 

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

 

이런 식으로 함께 사용할 수도 있다.

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

 

 

순수 JPA에서 사용하는 방법도 소개한다.

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

    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    private String username;
    private int age;
  
  ....
    @EntityGraph("Member.All")
    List<Member> findEntityGraph2ByUsername(@Param("username") String username);

 

이렇게도 작동한다.

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
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
글 보관함