1. 지연 로딩 (LAZY)
Member를 조회할 때 Team도 함께 조회해야 할까?
단순히 member 정보만 사용하는 비즈니스 로직
println(member.getName());
지연 로딩 LAZY을 사용해서 프록시로 조회
@Entity
public class Member {
@ManyToOne(fetch = FetchType.LAZY) //**
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
이렇게하면 Member는 DB에서 가져오고 Team은 프록시 객체로 가져온다 된다.
그리고 Team을 사용하는 시점에 DB에서 가져온다.
※ getTeam()에서는 Team을 가져오지 않고, Team의 필드나 메서드를 터치했을때 초기화된다.
2. 즉시 로딩 (EAGER)
Member와 Team을 자주 함께 사용한다면?
즉시 로딩 EAGER를 사용해서 함께 조회
@Entity
public class Member {
@ManyToOne(fetch = FetchType.EAGER) //**
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
즉시 로딩(EAGER), Member조회시 항상 Team도 조회
즉시 로딩의 경우 JOIN으로 한번에 조회할 수 있고, Member따로 Team따로 쿼리를 2번 날려서 조회할 수 있다.
JPA 구현체는 가능하면 조인을 사용해서 SQL 한번에 함께 조회한다.
3. 프록시와 즉시로딩 주의점
- 가급적 지연 로딩만 사용
- 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생 (연관된 테이블을 전부 조회하기 때문에)
- 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.
- ★★★@ManyToOne, @OneToOne은 기본이 즉시 로딩 -> LAZY로 설정★★★
- @OneToMany, @ManyToMany는 기본이 지연 로딩
즉시로딩은 JPQL에서 N+1 문제 상세설명
em.find는 PK를 가지고 조회하는 것이기 때문에 JPA가 내부적으로 join문을 사용해서 최적화해준다.
내부적으로 inner join문 하나가 날아가서 User가 조회됨과 동시에 Team까지 즉시로딩된다.
join문에 대한 쿼리를 만들어서 반환하는 경우 즉시로딩은 문제가 되지 않는다.
하지만 JPQL은 문제가 된다.
JPQL은 SQL로 번역이 된 후에 쿼리가 나간다. 만약 JPQL로 member를 조회하면 JPA는 member를 가져온다.
그런데 member안에 team이란 객체가 있고, 즉시로딩으로 설정되어있다는 것을 발견하면 JPA는 또 team을 조회하기
위해서 DB에 조회쿼리를 날리게 된다.
모든 User에 대해서 검색하고 싶어서 select 쿼리를 하나 날렸지만(1), 즉시로딩이 걸려있기 때문에 각각의 User가 가진 Team을 모두 검색한다(N)라는 N+1 문제가 발생한다. User가 10명이면 총 11번의 쿼리가 나간다는 것이다.
이것은 성능상 치명적인 문제이다.
ex)
SQL : select * from member;
SQL : selelct * from team where TEAM_ID = XXX
4. 즉시로딩 해결방안
- JPQL fetch join (대부분 이 방법으로 해결)
- 엔티티 그래프 (어노테이션)
- fetch size (쿼리가 한방 더 나가긴한다. 1+1)
5. 지연로딩 정리
- 모든 연관관계에 지연 로딩을 사용해라!
- 실무에서 즉시 로딩을 사용하지 마라!
- JPQL fetch 조인이나, 엔티티 그래프 기능을 사용해라! (뒤에서 설명)
- 즉시 로딩은 상상하지 못한 쿼리가 나간다
※출처 - 인프런 김영한 JPA
'JPA' 카테고리의 다른 글
JPA - 기본값 타입과 임베디드 타입 (0) | 2022.04.12 |
---|---|
JPA - 영속성 전이(CASCADE)와 고아 객체 (2) | 2022.04.11 |
JPA - 프록시 (0) | 2022.04.11 |
JPA - @MappedSuperclass (0) | 2022.04.10 |
JPA - 상속관계 매핑 (0) | 2022.04.10 |
댓글