[JPA] 지연로딩과 즉시로딩

Y_Sevin·2022년 1월 24일
0

참고
지연로딩 즉시로딩
n+1

즉시로딩

엔티티를 조회할때 연관관계에 있는 데이터를 한번에 모두 로딩하겠다는 의미입니다.

즉시로딩( EAGER )은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵습니다. 특히 JPQL을 실행할 때 N+1문제가 자주 발생합니다.

왜 어떤 SQL이 실행될지 추적이 어렵냐면 예를 들어 Member 를 조회한다고 가정했을때 즉시로딩을 사용하면 연관된 order또한 한번에 조회하게됩니다. 그리고 이 order를 조회할때 연관되어있는 것을 모두 조회하며 줄줄히 조회하게 되는 문제가 발생합니다.

또한 Member 를 조회했을때 연관관계에 있는 데이터를 모두 조회한다면 불필요한 sql쿼리가 날아갑니다.

# 즉시로딩
@ManyToOne(fetch = FetchType.EAGER)

# 지연로딩
@ManyToOne(fetch = FetchType.LAZY)

em.find()는 PK를 정해놓고 DB에서 가져오기 때문에 JPA 내부에서 최적화를 할 수 있습니다.

하지만 실무에서 자주 사용하는 JPQL일 경우에는 JPQL : select o from order o; SQL : select * from order; 이런식으로 번역이 되버리는 문제가 발생합니다. 이렇게 된다면 조회 결과가 백개일때 백개의 조회 쿼리가 발생하는 N+1문제가 발생합니다.

지연로딩( LAZY )

아래와 같이 지연 로딩으로 설정하고 Member를 조회해보면 즉시 로딩 방식과 달리 연관되어있는 데이터를 조회하는 쿼리가 생성되지 않고 Member를 조회하는 쿼리만 나가게 됩니다. 그리고 연관되어있는 데이터는 실제 해당 데이터를 사용하는 시점에 조회하는 쿼리가 나갑니다.

 @ManyToOne(fetch = FetchType.LAZY) //Team을 조회할 때 지연로딩을 사용하곘다!
 @JoinColumn(name = "team_id")
 Team team;

JPQL로 Member조회

Member findMember = em.createQuery("select m from Member m", Member.class).getSingleResult();

실제 SQL 코드

//Team을 조회하는 쿼리가 나가지 않음!
select
    member0_.id as id1_0_,
    member0_.team_id as team_id3_0_,
    member0_.username as username2_0_ 
from
    Member member0_

때문에 실무에서 모든 연관관계는 지연로딩( LAZY )으로 설정해야 합니다.

JPA는 연관된 엔티티를 함께 DB에서 조회해야하는 경우가 발생하면, fetch join 또는 엔티티 그래프 기능을 사용해 해당 정보를 가져옵니다.

@XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정해야 합니다.

OneToMany는 기본이 LAZY입니다.

지연로딩이 가끔 트렌젝션밖에서 안되는 이슈가 가끔있는데 트렉젝션을 빨리가져온다던가 OSIV방식을 쓰는 해결방법이 있다고 합니다.

profile
매일은 아니더라도 꾸준히 올리자는 마음으로 시작하는 개발블로그😎

0개의 댓글