본 포스팅은 인프런 - 자바 ORM 표준 JPA 프로그래밍 (기본편) 을 강의를 바탕으로 공부하고 정리한 글입니다.
즉시로딩 vs 지연 로딩
회원을 저장하고 조회해오는 간단한 로직이다.
즉시로딩을 사용했을 때와 지연로딩을 사용했을 때의 차이점이 무엇일까?
Member member = new Member();
member.setUsername("member");
em.persist(member); // 저장
em.flush();
em.clear();
Member findMember = em.find(Member.class, member.getId()); // 조회
tx.commit();
지연로딩
@Entity
public class Member extends BaseEntity {
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
}
- fetch 타입을 LAZY로 설정해 지연로딩을 설정해주었다.
Hibernate:
select
member0_.member_id as member_i1_3_0_,
member0_.createdBy as createdb2_3_0_,
member0_.createdDate as createdd3_3_0_,
member0_.lastModifiedBy as lastmodi4_3_0_,
member0_.lastModifiedDate as lastmodi5_3_0_,
member0_.team_id as team_id7_3_0_,
member0_.user_name as user_nam6_3_0_
from
Member member0_
where
member0_.member_id=?
- 지연로딩 사용 시 member만 조회되는 것을 확인할 수 있다.
System.out.println("findMember.getTeam().getClass() = " + findMember.getTeam().getClass());
System.out.println("===============");
findMember.getTeam().getName(); // proxy 초기화
System.out.println("===============");
findMember.getTeam().getClass() = class hellojpa.Team$HibernateProxy$uwKLjvqw
===============
Hibernate:
select
team0_.team_id as team_id1_7_0_,
team0_.createdBy as createdb2_7_0_,
team0_.createdDate as createdd3_7_0_,
team0_.lastModifiedBy as lastmodi4_7_0_,
team0_.lastModifiedDate as lastmodi5_7_0_,
team0_.name as name6_7_0_
from
Team team0_
where
team0_.team_id=?
===============
- team은 지연로딩으로 가져오기 때문에 프록시 객체가 조회되었다.
- team.getName()같이 실제로 team을 사용하는 시점에 select 쿼리가 나가며 프록시 객체가 초기화 된다.
이렇게 지연로딩을 사용하면 member와 team을 조회해올 때 select문이 2번 나가게 된다.
member와 team을 함께 사용하는 일이 드문 경우라면 지연로딩을 사용하는 것이 적합하지만,
만약 member와 team을 자주 함께 사용하는 경우라면 select 쿼리가 두번 나가는 지연로딩보다 한꺼번에 조회해오는 즉시로딩이 더 적합할 수 있다.
즉시로딩
@Entity
public class Member extends BaseEntity {
...
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "team_id")
private Team team;
}
- fetch 타입을 EAGER로 설정해 즉시로딩을 설정해주었다.
Hibernate:
select
member0_.member_id as member_i1_3_0_,
member0_.createdBy as createdb2_3_0_,
member0_.createdDate as createdd3_3_0_,
member0_.lastModifiedBy as lastmodi4_3_0_,
member0_.lastModifiedDate as lastmodi5_3_0_,
member0_.team_id as team_id7_3_0_,
member0_.user_name as user_nam6_3_0_,
team1_.team_id as team_id1_7_1_,
team1_.createdBy as createdb2_7_1_,
team1_.createdDate as createdd3_7_1_,
team1_.lastModifiedBy as lastmodi4_7_1_,
team1_.lastModifiedDate as lastmodi5_7_1_,
team1_.name as name6_7_1_
from
Member member0_
left outer join
Team team1_
on member0_.team_id=team1_.team_id
where
member0_.member_id=?
- 쿼리를 확인해보면 member를 조회해올 때 join을 사용해 한번에 team까지 조회해오는 것을 확인 할 수 있다.
가급적 지연로딩을 사용하자!
- 실무에서 즉시 로딩 사용은 지양해야 하며, 모든 연관관계에서 지연 로딩 사용을 권장한다.
- 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생한다.
- 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.
- JPQL fetch 조인이나, 엔티티 그래프 기능을 사용해 N+1 문제 해결 가능 (관련하여 추후 포스팅할 예정)
- @ManyToOne, @OneToOne은 기본이 즉시 로딩으로 되어있어, 따로 지연 로딩(LAZY)으로 설정이 필요하다.
- @OneToMany, @ManyToMany는 기본이 지연 로딩으로 되어있어, 따로 설정하지 않아도 된다.
'🌱 Spring > JPA' 카테고리의 다른 글
[JPA] JPQL : 기본 문법 (0) | 2022.10.29 |
---|---|
[JPA] JPA가 지원하는 쿼리 방법 (JPQL, Criteria, QueryDsl) (0) | 2022.10.29 |
[JPA] 값 타입 (0) | 2022.10.19 |
[JPA] 영속성 전이(CASECADE)와 고아 객체 (0) | 2022.10.18 |
[JPA] 프록시 (0) | 2022.10.18 |
[JPA] 상속관계 매핑(@Inheritance), 매핑 정보 상속@MappedSuperclass) (0) | 2022.10.17 |
[JPA] 연관관계 매핑(3) - 다중성 (ManyToOne, ManyToOne, OneToOne, ManyToMany) (1) | 2022.10.11 |
[JPA] 연관관계 매핑(2) : 양방향 매핑 (0) | 2022.10.06 |