Posts [자바 ORM 표준 JPA 프로그래밍-기본편] 즉시 로딩과 지연 로딩
Post
Cancel

[자바 ORM 표준 JPA 프로그래밍-기본편] 즉시 로딩과 지연 로딩

본 포스팅은 인프러의 JPA 기본편을 수강하고 정리하는 내용입니다.


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

단순히 member 정보만 사용하는 비즈니스 로직 println(member.getName()) 메소드가 있다고 해보자.

image

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

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;
    @Column(name = "USERNAME")
    private String name;
    @ManyToOne(fetch = FetchType.LAZY) //**
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    ..
}

image

  • 지연로딩으로 세팅하면 연관된 것을 프록시로 가져온다. (Team 멤버 변수)

지연 로딩 (Lazy Loading)

image

image

1
2
Team team = member.getTeam();
team.getName(); // 실제 team을 사용하는 시점에 초기화(DB 조회)

반대로 Member와 Team을 자주 함께 사용한다면?

image

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

image

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;
    @Column(name = "USERNAME")
    private String name;
    @ManyToOne(fetch = FetchType.EAGER) //**
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    ..
}

image

  • Member랑 Team을 한 방에 같이 조회한다.
  • Team의 프록시 타입을 조회해보면 실제 Team 엔티티가 출력된다.
  • JPA 구현체는 가능하면 조인을 사용해서 SQL 한번에 함께 조회
    • 만약 애플리케이션 개발시 두 개가 무조건 전부 필요한 경우라면 Eager 로딩 방식을 적용하면 된다. image

프록시와 즉시로딩 주의

  • 가급적 지연 로딩만 사용(특히 실무에서)
    • 실무에서 테이블이 복잡하게 얽혀있을 수 밖에 없기 때문에 가급적이 아니라 무조건!!!
  • 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생
    • 조인이 한 두개라면 문제가 되진 않지만 5개가 조인이 걸린다하면 쿼리가 엄청 예상치 못한 쿼리가 날라간다..
  • 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.
    • 1)
  • @ManyToOne, @OneToOne은 기본이 즉시 로딩
    • => LAZY로 설정
    • JPQL로 Member를 조회한다고 해보자. ~~~ Team team = new Team(); team.setName(“teamA”); em.persist(team);

    Team teamB = new Team(); teamB.setName(“teamB”); em.persist(teamB);

    Member member1 = new Member(); member1.setUsername(“memberS1”); member1.setTeam(team); em.persist(member1);

    Member member2 = new Member(); member2.setUsername(“memberS1”); member2.setTeam(teamB); em.persist(member2);

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

    List members = em.createQuery("select m from Member m", Member.class) .getResultList(); tx.commit();

    1
    2
    3
    
    - JPQL은 `em.createQuery("select m from Member m", Member.class)` 를 실행할 때 먼저 SQL로 번역한다. 그러면 당연히 Member 테이블만 select한다.
    -  Member를 가져왔더니 Team이 즉시로딩되있네? 그러면 Member쿼리나가고 Member쿼리가 10개면 10개만큼 Team테이블을 조회하는 쿼리가 나간다.
    

    select * from member select * from Team where TEAM_ID = xxx ~~~

    • image
    • N(추가 쿼리: 실제 첫 쿼리에서 조회된 데이터의 갯수) + 1 (첫 쿼리)
    • Lazy로 변경하면 위의 예제 코드에선 이와 같은 문제가 나오지 않는다. (Team은 프록시로만 다 박혀있게됨)
    • 하지만 여기서 Team을 루프로 돌리면 Team 테이블을 조회하는 쿼리가 계속 나갈 것이다.
    • 일단은 모든 연관 관계를 지연 로딩으로 다 깔고 fetch join, @entity graph, batch size와 같은 3가지 방법으로 해결할 수 있다.
    • 이에 관련한 내용은 여기를 참고하자.
  • @OneToMany, @ManyToMany는 기본이 지연 로딩
    • @ManyToOne, @OneToOne은 기본이 즉시 로딩이기에 다 Lazy로 설정을 해줘야한다.

Note: 실무에선 lazy로 다 바르고 딱 필요해서 동시에 한 방에 조회할 필요가 있을때 fetch join으로 가져오도록 하자.

지연 로딩 활용

이 부분은 굉장히 이론적인 내용이고 실무에선 다 지연로딩으로 설정해야 한다.

image

  • Member와 Team은 자주 함께 사용 -> 즉시 로딩
  • Member와 Order는 가끔 사용 -> 지연 로딩
  • Order와 Product는 자주 함께 사용 -> 즉시 로딩

image

teamA는 실제 엔티티가 들어오고 orders는 프록시로 들어오게 된다.

지연 로딩 활용 - 실무

  • 모든 연관관계에 지연 로딩을 사용해라!
  • 실무에서 즉시 로딩을 사용하지 마라!
  • JPQL fetch 조인이나, 엔티티 그래프 기능을 사용해라!(뒤에서 설명)
  • 즉시 로딩은 상상하지 못한 쿼리가 나간다.
This post is licensed under CC BY 4.0 by the author.

[자바 ORM 표준 JPA 프로그래밍-기본편] 프록시와 연관관계 관리

[자바 ORM 표준 JPA 프로그래밍-기본편] 영속성 전이: CASCADE