Backend/Spring
JPA 페치(Fetch) 전략 - 즉시 로딩(EAGER)과 지연 로딩(LAZY)
Seyun(Marco)
2020. 12. 15. 00:27
728x90
JPA 페치(Fetch) 전략 - 즉시 로딩(EAGER)과 지연 로딩(LAZY)
예제코드 세팅
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
@Entity
public class Member {
@Id
private Long id;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
private String name;
public Member() {
}
public Member(final Long id, final Team team, final String name) {
this.id = id;
this.team = team;
this.name = name;
}
public Long getId() {
return id;
}
public Team getTeam() {
return team;
}
public String getName() {
return name;
}
}
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Team {
@Id
private Long id;
private String name;
public Team() {
}
public Team(final Long id, final String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class FetchTest {
@DisplayName("Fetch 테스트")
@Test
void SaveAndFindForObj() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
final EntityManager em = emf.createEntityManager();
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
final Team team = new Team(1L, "Team A");
em.persist(team);
final Member member = new Member(1L, team, "Member A");
em.persist(member);
em.flush();
em.clear();
final Member findMember = em.find(Member.class, member.getId());
final Member findMember = em.find(Member.class, member.getId());
final Team findTeam = findMember.getTeam();
System.out.println(findTeam);
assertAll(
() -> assertThat(findMember.getId()).isEqualTo(member.getId()),
() -> assertThat(findMember.getTeam().getId()).isEqualTo(member.getTeam().getId()),
() -> assertThat(findMember.getName()).isEqualTo(member.getName())
);
transaction.commit();
} catch (final Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
즉시 로딩(EAGER)
- 엔티티를 조회할 떄 연관된 엔티티도 함께 조회한다.
- 즉시로딩을 사용하면 실제 team의 엔티티를 불러온다.
- 즉시 로딩을 최적화하기 위해 가능하면 조인쿼리를 사용한다.
@ManyToOne(fetch = FetchType.EAGER)
- 위와 같이 Member에 넣으면 아래와 같은 SQL이 실행되는걸 확인할 수 있습니다.
Hibernate:
select
member0_.id as id1_0_0_,
member0_.name as name2_0_0_,
member0_.team_id as team_id3_0_0_,
team1_.id as id1_1_1_,
team1_.name as name2_1_1_
from
Member member0_
left outer join
Team team1_
on member0_.team_id=team1_.id
where
member0_.id=?
JPA 조인 전략
- 내부 조인(INNER JOIN)이 아닌 외부 조인(LEFT OUTER JOIN)을 사용하는 것을 볼 수 있다.
- 회원 테이블에서 TEAM이 null을 허용하고 있기 때문에 팀이 속하지 않은 회원까지 조회해야 하기 때문에 외부 조인을 사용한다.
- 그러나 외부 조인보다 내부 조인이 성능과 최적화에서 더 뛰어나다. 그렇다면 내부 조인을 사용하려면 어떻게 해야 하는가?
- 바로 NOT NULL 제약 조건을 설정하면 된다.
- 아래와 같이 값을 준다면, 그 아래와 같이 내부 조인 SQL을 확인할 수 있다.
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "team_id", nullable = false)
select
member0_.id as id1_0_0_,
member0_.name as name2_0_0_,
member0_.team_id as team_id3_0_0_,
team1_.id as id1_1_1_,
team1_.name as name2_1_1_
from
Member member0_
inner join
Team team1_
on member0_.team_id=team1_.id
where
member0_.id=?
지연 로딩(LAZY)
- 연관된 엔티티를 실제 사용할 때 조회한다.
- 이떄 실제 TEAM 객체가 아닌 프록시 TEAM 객체를 넣어놓는다.
@ManyToOne(fetch = FetchType.LAZY)
- 위와 같이 Member에 넣으면 아래와 같은 SQL이 실행되는걸 확인할 수 있습니다.
Hibernate:
select
member0_.id as id1_0_0_,
member0_.name as name2_0_0_,
member0_.team_id as team_id3_0_0_
from
Member member0_
where
member0_.id=?
Hibernate:
select
team0_.id as id1_1_0_,
team0_.name as name2_1_0_
from
Team team0_
where
team0_.id=?
JPA 기본 Fetch 전략
즉시 로딩(FetchType.EAGER)
- @ManyToOne
- @OneToOne
지연 로딩(FetchType.LAZY)
- @OneToMany
- @ManyToMany
결론
- 컬렉션에서는 무조건 지연로딩을 사용하고 단일 엔티티는 즉시 로딩을 사용한다.
- 그러나 추천하는 방법은 모든 연관관계에 지연 로딩을 사용하고 필요한 곳에만 즉시 로딩을 사용하는 것이다.
- 성능적으로 좀 더 좋게 사용할 수 있기 때문이다.
참고자료
728x90
728x90