즉시 로딩과 지연 로딩

현시기얌·2021년 12월 5일
0

JPA

목록 보기
13/14

Question) Member를 조회할 때 Team도 함께 조회해야 할까?

지연 로딩 LAZY를 사용해서 프록시로 조회

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Member extends BaseTimeEntity{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;
}

Fetch = LAZY를 사용해 Member 객체만 실제 Entity로 가져오고 Team 객체는 빈껍데기인 프록시 객체로 가져온다.

LAZY 테스트

final Team team = Team.of("teamA");
em.persist(team);
            
final Member member = new Member();
member.setName("hello");
member.addTeam(team);

em.persist(member);

         
em.flush();
em.clear();


final Member findMember = em.find(Member.class, member.getId());
System.out.println("findMember.getClass() = " + findMember.getClass());
System.out.println("findMember.getTeam().getClass() = " + findMember.getTeam().getClass());

System.out.println("=====================");
findMember.getTeam().getName();
System.out.println("=====================");

실행 결과


  • 쿼리를 보면 Member 객체의 값들만 select 해서 가져오는 것을 확인할 수 있다. 객체를 가져오는 것을 확인할 수 있다.(값 X)
  • Team 객체의 값들이 필요할 때 그때서야 DB에서 쿼리를 조회해서 값을 가져오는 것을 확인할 수 있다.

지연 로딩 LAZY을 사용해서 프록시로 조회

![](https://velog.velcdn.com/images%2F!%5B%5D(https%3A%2F%2Fimages.velog.io%2Fimages%2Fhyun6ik%2Fpost%2Fc43bd2fb-88f1-452e-8b6e-9706698dab18%2Fimage.png)

Quesetion) Member와 Team을 자주 함께 사용한다면?

즉시 로딩 EAGER를 사용해서 함께 조회

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Member extends BaseTimeEntity{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "team_id")
    private Team team;
}

EAGER 테스트

final Team team = Team.of("teamA");
em.persist(team);
            
final Member member = new Member();
member.setName("hello");
member.addTeam(team);

em.persist(member);

         
em.flush();
em.clear();


final Member findMember = em.find(Member.class, member.getId());
System.out.println("findMember.getClass() = " + findMember.getClass());
System.out.println("findMember.getTeam().getClass() = " + findMember.getTeam().getClass());

System.out.println("=====================");
findMember.getTeam().getName();
System.out.println("=====================");

실행 결과

  • Member를 가져올 때 Team도 조인해서 같이 가져오는 것을 확인할 수있다.
  • 또한 Member와 연관관계인 Team이 프록시 객체가 아닌 실제 엔티티인 것을 확인할 수 있다. (값 O)

즉시 로딩(EAGER), Member 조회시 항상 Team도 조회

프록시 즉시 로딩 주의점

가급적 지연 로딩(LAZY)를 사용해야 한다.

1. 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생한다.

만약 Member와 연관관계가 Team 뿐만 아니라 수십개 있다고 가정했을 때
Member 객체를 조회했을 뿐인데 수 많은 조인 컬럼들이 생기게 된다.
이는 성능에도 문제를 일으킬 수 있다.

2. 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.

 final List<Member> memberList = 
 em.createQuery("select m from Member m", Member.class)
                    .getResultList();

실행 결과

SQL : select * from Member 
SQL : select * from Team where TEAM_ID = xxx
  • JPQL을 사용해서 Member 클래스들 모두 조회해서 가져왔는데 Member와 연관되어 있는 Team이 EAGER로 되어있기 때문에 DB에서 Team을 다시 한번 가져오게 된다.

예제 코드

final Team teamA = Team.of("teamA");
em.persist(teamA);

final Team teamB = Team.of("teamB");
em.persist(teamB);

final Member member1 = new Member();
member1.setName("hello");
member1.addTeam(teamA);

final Member member2 = new Member();
member2.setName("hello");
member2.addTeam(teamB);

em.persist(member1) ;
em.persist(member2);

em.flush();
em.clear();

final List<Member> memberList = em.createQuery("select m from Member m", Member.class)
                    .getResultList();

실행 결과

Member 객체를 조회하고 난 후 Member 객체와 연관관계맺은 Team 객체가 다르다면 각각 PK를 통해서 다시 찾아야 하므로 Team을 조회하는 쿼리가 계속 늘어나게 된다.

때문에 이러한 문제를 방지하기 위해 연관관계는 LAZY로 바꾸고 fetch Join을 사용해서 한번에 가져와야한다.

 final List<Member> memberList = 
 em.createQuery("select m from Member m join fetch m.team", Member.class)
                    .getResultList();

페치 조인 실행 결과

페치 조인 한방 쿼리로 Member와 Team 객체의 값을 한번에 모두 가져온다.

3. @ManyToOne, @OneToOne은 기본이 즉시 로딩이므로 LAZY로 바꿔줘야한다.

4. @OneToMany, @ManyToMany는 기본이 지연로딩이다.

profile
현시깁니다

0개의 댓글