지연 로딩 (Lazy loading)
연관된 내용을 프록시로 가져온다 .
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
}
연관된 필드의 연관관계 맵핑 옵션으로 fetch 타입을 지정할 수 있다. Lazy (지연로딩), Eagar(즉시로딩)이 있다.
//팀 저장
Team team = new Team();
team.setName("team1");
em.persist(team);
//멤버 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);
em.flush();
em.clear();
Member member1 = em.find(Member.class, member.getId());
System.out.println("member1.getTeam().getClass() = " + member1.getTeam().getClass());
//프록시 초기화 시점
System.out.println("----------초기화 시점-----------");
String teamName = member1.getTeam().getName();
System.out.println("---------------------");
System.out.println("teamName = " + teamName);
System.out.println("---------------------");
team에 필드에 대한 내용에 접근할 때 초기화가 되기는데 위에서는 클래스 정보에 접근하는 것이기 때문에 초기화는 되지 않는다.
어떤 클래스가 출력될까?
지연로딩이 설정되어 있기 떄문에 프록시객체가 리턴되었다 .
초기화 시점에 em.clear로 영속성 컨텍스트가 빈 상태이기 떄문에 쿼리가 날라가고 proxy객체가 참조하는 실제 엔티티를 통해 target.getName()이 호출되어 값이 리턴되었다 .
즉시로딩 (EAGAR)
연관된 내용을 한번에 가져온다 .
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.EAGAR)
@JoinColumn(name = "team_id")
private Team team;
}
//팀 저장
Team team = new Team();
team.setName("team1");
em.persist(team);
//멤버 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);
em.flush();
em.clear();
Member member1 = em.find(Member.class, member.getId());
System.out.println("member1.getTeam().getClass() = " + member1.getTeam().getClass());
String teamName = member1.getTeam().getName();
System.out.println("teamName = " + teamName);
결과
조인으로 한번에 다가져왔다 . 팀의 클래스도 진짜 객체가 들어간다.
프록시와 즉시로딩 주의점
- 가급적 지연 로딩만 사용하는 것이 좋다.
(예상치 못한 SQL이 나온다.)
- 즉시로딩은 JPQL에서 N+1 문제를 일으킨다 .
- em.find() 등의 JPA메서드는 이미 즉시로딩에 대해 최적화가 되어 있기 때문에 조인으로 한방에 데이터를 가져오지만
JPQL같은 경우에는 JPQL이 SQL로 변환되어서 날라가기 떄문에 그 안에 어떤 연관관계가 있는지는 JPA가 알 수가 없다 .
예시) "select member from member m" <-JPQL
예시의 JPQL처럼 member 는 팀과 연관관계가 맺어 있지만 쿼리가 날라갈떄는 "select * from member" 이렇게만 날라가기 때문에 어떤 연관관계를 갖고있는지는 알지 못한다 . 떄문에 member 데이터를 가지고오고나서 팀과의 연관관계를 보고 팀을 찾는 한번 더 날린다. 만약 연관관계가 4개다 하면 멤버를 가지고와서 4개 쿼리를 다시날리는 거다 . 이를 N+1 문제라고 한다.
*해결방안: 처음에 lazyLoding 으로 모두 설정하고 필요한경우 JPQL의 fetch조인으로 가져온다.
- @ManyToOne , @OneToOne 은 기본이 즉시 로딩이다.
'JPA' 카테고리의 다른 글
JPA 공부 11 - JPA 데이터 타입 분류 (임베디드 타입) (0) | 2021.06.05 |
---|---|
JPA 공부 10 - 영속성 전이 (cascade) (0) | 2021.06.05 |
JPA 공부 8 - Proxy (0) | 2021.06.05 |
JPA 공부 7 - 상속관계 매핑 (0) | 2021.06.05 |
JPA 공부 6 - 다대다 연관관계(N:N) (0) | 2021.06.04 |