Spring Boot :: 연관관계 매핑

hyunjoon park·2024년 1월 26일
1

Spring Boot

목록 보기
8/10

연관관계 매핑

즉시로딩과 지연로딩

이해를 돕기 위해 Member 엔티티와 Team 엔티티를 사용해보자

하나의 Member는 하나의 Team을 가질 수 있고
하나의 팀은 여러 Member를 가질 수 있기 때문에 이 관계는 1 : N 관계이다

  • @OneToMany의 default fetch 설정은 Lazy(지연로딩)이다
    -> @XxToMany Many로 끝나면 default가 FetchType.LAZY (OneToMany, ManyToMany)

  • @ManyToOne의 default fetch 설정은 Eager(즉시로딩)이다
    -> @XxToOne One으로 끝나면 default가 FetchType.EAGER (OneToOne, ManyToOne)

@Entity
@Table @Getter @ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
   @Id
   @Column(name = "member_id")
   private Long id;
   private String userName;
 
   @ManyToOne
   @JoinColumn(name = "team_id")
   private Team team;
  
  // Getter, Setter, Constructor...
}
@Entity
@Table @Getter @ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Team {
	@Id
    @Column(name = "team_id")
    private Long id;
    private String name;
    
    // Getter, Setter, Constructor
}

즉시로딩 (FetchType.EAGER)

@ManyToOne의 default 설정값이 FetchType.EAGER인데 따로 설정을 안해줬기 때문에 즉시로딩이다
-> 동시에 조회가 된다
-> 대부분의 JPA 구현체는 즉시로딩을 최적화하기 위해 가능하면 조인 쿼리를 사용한다

SELECT m.member_id, m.team_id, m.userName, t.team_id, t.name
FROM 
	Member m
	LEFT OUTER JOIN
	Team t
	ON m.team_id = t.team_id
WHERE m.member_id = 0;

지연로딩 (FetchType.LAZY)

로딩되는 시점에 Lazy 로딩 설정이 되어있는 Team 엔티티는 프록시 객체로 가져온다

  • findAll을 호출 시
    Member 객체를 탐색하려 할 때 n+1 문제 발생
@Entity
public class Member {
   @Id
   @Column(name = "member_id")
   private Long id;
 
   @Column(name = "user_name")
   private String userName;
 
   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "team_id")
   private Team team;
  
  // Getter, Setter, Constructor...
}
@Entity
public class Team {
	@Id
    @Column(name = "team_id")
    private Long id;
    
    @Column(name = "name")
    private String name;
    
    // Getter, Setter, Constructor
}

System.out.println("1. 회원 객체 조회");
Member member = entityManager.find(Member.class, 0L);

System.out.println("2. 회원 이름 조회");
System.out.println("회원 이름 : " + member.getUsername());

System.out.println("3. 팀 객체 조회");
Team team = member.getTeam(); // Proxy 객체이기 때문에 실제 조회는 team.getXXX 할때 발생합니다.
System.out.println(team.getClass());

System.out.println("4. 팀 이름 조회");
System.out.println("팀 이름 : " + team.getName()); // Proxy 객체 초기화
System.out.println(team.getClass());

실행하면

1. 회원객체 조회

SELECT m.member_id, m.team_id, m.user_name
FROM 
	Member m
WHERE m.member_id = 0;

2. 회원 이름 조회
회원 이름 : 회원1

3. 팀 객체 조회
class TIL.jpa.Domain.Team$HibernateProxy$hbJa4wRu

4. 팀 이름 조회

SELECT t.team_id, t.name
FROM
	Team t
WHERE t.team_id = 0;

팀 이름 :1
class TIL.jpa.Domain.Team$HibernateProxy$hbJa4wRu

다음과 같이 출력되는데

해결방법 : JPQL을 사용하여 DB에서 데이터를 가져올 때 처음부터 연관된 데이터까지 같이 가져오게 하는 방법

@Query() 어노테이션을 사용한다

@Query("SELECT m FROM Member m JOIN FETCH m.team")
List<Member> members = findAllFetchTeam();
profile
Backend Developer

0개의 댓글