JPA에서 데이터를 조회할 때 사용하는 방법인 즉시 로드 및 지연 로드에 대해 알아봅니다.
FetchType
FetchType은 단일 엔터티를 쿼리할 때 관련 엔터티를 데이터베이스에서 가져오는 방법을 결정하는 옵션입니다.
FetchType에는 두 가지 유형이 있습니다.
- Eager
- Lazy
@OneToOne과 @ManyToOne의 default FetchType은 Eager이며,
@ManyToOne 및 @ManyToMany의 기본 FetchType은 Lazy입니다.
즉시 로딩(Eager Loading)
즉각적인 로드는 한 엔티티를 조회할 때 모든 관련 엔티티를 한 번에 호출하는 것입니다.
- ex) @ManyToOne (fetch = FetchType.EAGER)
(예)
회원과 리뷰에 관계를 생각해 봅시다.
한 멤버가 여러 리뷰를 남길 수 있기 때문에 멤버: 리뷰 관계는 1:N 관계입니다.
@Entity
@Getter
public class Member {
@Id @GeneratedValue
@Column(name = "memberId")
private Long id;
private String name;
@JsonIgnore
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<MemberReview> review = new ArrayList<>();
}
@Entity
@Getter
public class MemberReview {
@Id @GeneratedValue
@Column(name = "reviewId")
private Long id;
private String content;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "userId")
private Member member;
}
MemberReview memberReview = em.find(MemberReview.class, reviewId);
리뷰 보기
select
memberrevi0_.review_id as review_i1_7_0_,
memberrevi0_.content as content2_7_0_,
memberrevi0_.user_id as user_id3_7_0_,
member1_.member_id as member_i1_6_1_,
member1_.name as name2_6_1_
from
member_review memberrevi0_
left outer join
member member1_
on memberrevi0_.user_id=member1_.member_id
where
memberrevi0_.review_id=?
검토를 조회할 때 검토와 연관된 구성원까지 데이터베이스에서 로드되는지 확인할 수 있습니다.
지연 로딩(Lazy Loading)
지연 로딩은 필요에 따라 관련 엔티티를 호출하는 것입니다.
- ex) @MaynToOne (fetch = FetchType.LAZY)
위의 예에서 MemberReview의 FetchType만 LAZY로 변경되었습니다.
@Entity
@Getter
public class MemberReview {
@Id @GeneratedValue
@Column(name = "reviewId")
private Long id;
private String content;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "userId")
private Member member;
}
MemberReview memberReview = em.find(MemberReview.class, reviewId);
System.out.println(memberReview.getMember().getId()); //이때, 멤버 조회 쿼리 실행
System.out.println(memberReview.getMember().getName());
select
memberrevi0_.review_id as review_i1_7_0_,
memberrevi0_.content as content2_7_0_,
memberrevi0_.user_id as user_id3_7_0_
from
member_review memberrevi0_
where
memberrevi0_.review_id=?
select
member0_.member_id as member_i1_6_0_,
member0_.name as name2_6_0_
from
member member0_
where
member0_.member_id=?
LAZY의 경우 멤버를 사용할 때 멤버 쿼리가 표시되는지 확인할 수 있습니다.
어느 것을 사용해야합니까?
실무에서는 지연 로딩(Lazy Loading)을 주로 사용합니다.
그 이유는 예를 들어 MemberReview와 연관된 엔티티가 하나가 아닌 N개라고 가정합니다.
즉시 로드하면 MemberReview 하나를 쿼리할 때마다 불필요한 다른 N개의 엔터티를 쿼리하는 쿼리 N개가 생성됩니다.
즉, 하나의 JPQL문에 대해 N+1개의 총 조회문이 실행됩니다.
이것을 N+1 문제라고 합니다.
이 경우 즉시 로딩은 성능이 매우 저하되므로 가능한 한 실무에서는 지연 로딩을 사용하는 것이 좋습니다!
참고 자료