[JPA TIL] 즉시 로딩과 지연 로딩(FetchType.LAZY / EAGER)

Hyebin Lee·2022년 4월 6일
0

JPA TIL

목록 보기
5/7
post-thumbnail

A와 B 테이블이 서로 연관관계 매핑이 되어있다고 해보자.
비즈니스 로직에서 단순히 A 로직만 사용하고 싶은데 B까지 함께 조회되면 손해이다.
JPA는 이 문제를 지연로딩 LAZY를 사용해서 프록시로 조회하는 방법으로 해결한다.

코드로 이해하기

🌷FetchType.LAZY

  • Entity A와 B 사이가 다대일 @ManyToOne 관계로 매핑되어 있는 상황에서 FetchType 를 설정해줄 수 있다.
@Entity
@Getter
@Setter
public class A extends BaseEntity {@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;private Integer number;
    private String testname;// 패치 타입 LAZY 설정
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "b_id")
    private B b;}

메인 함수에서 A만을 조회하여 aTemp 객체에 담은 뒤, aTemp 객체의 B객체에 접근하면 해당 객체는 Proxy 객체이다.

  • 이 때 aTemp는 아직 B객체의 필드에 접근하지 않았다
  • 따라서 B 객체에 대한 쿼리는 나가지 않는다
  • B 객체는 Proxy 상태로 남아있다.
A aTemp = em.find(A.class, a.getId());
System.out.println(aTemp.getB().getClass()); // Proxy 객체 프린트 - $HibernateProxy$, B객체의 필드에 접근하지 않음 (쿼리 안나감)

LAZY 로딩에서 B 테이블에 대한 조회 쿼리는 A 객체에서 B객체의 필드에 접근하는 순간 나간다

A aTemp = em.find(A.class, a.getId());
System.out.println(aTemp.getB().getClass());// 아직 프록시 객체, 필드에 접근하지 않음 
System.out.println(aTemp.getB().getNumber()); //이 때 select * from B b로 쿼리 나감 

🌿Fetch.LAZY의 장단점

  • B 에 대한 정보 필요 없이 A만 조회하고 싶을 때 성능이 좋다
  • 하지만 B에 대한 정보까지 조회하는 순간 , SELECT 쿼리가 한 번 더 나가게 된다. (그만큼 성능상 손해를 본다)

🌷FetchType.EAGER

  • LAZY 로딩과 다르게 A를 조회하는 순간 A와 연관관계를 맺고 있는 B까지 같이 SELECT 되어서 조회된다.

프록시와 즉시 로딩 주의할 점

  1. 실무에서는 가급적 지연 로딩만 사용한다 : 즉시 로딩을 적용하면 예상치 못한 SQL이 발생한다. @ManyToOne이 5개 있는데 전부 EAGER로 설정되어 있으면 조인이 5개나 일어난다. 실무에서는 테이블 연관관계가 복잡하고 많기 떄문에 웬만하면 지연로딩을 지향한다.
  2. 즉시로딩은 JPQL에서 N+1 문제를 일으킨다 : JPQL은 query string이 그대로 SQL로 변환되기 때문에 select a from A a만 보면 당연히 Member만 select 하게 된다. 근데 EAGER타입으로 설정되어 있음을 시스템상에서 뒤늦게 알게 되면 뒤늦게 B까지 SELECT 한다. 이럴거면 LAZY 로딩이 훨씬 낫다.

결론 : 즉시로딩 말고 LAZY로딩과 JPQL의 fetch join을 쓰자

실무에서는 LAZY 로딩 전략으로 쓴다
근데 대부분 A와 B를 함께 조회해서 쓰기 때문에 LAZY로딩이 비효율적이다 싶은 경우에는 어떻게 해야할까?
이런 경우 JPQL의 fetch join을 통해서 해당 시점에 한방 쿼리로 가져와서 쓸 수 있다.

0개의 댓글