Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

즉시 로딩과 지연 로딩. 본문

인프런/[인프런] 자바ORM 표준 JPA 프로그래밍

즉시 로딩과 지연 로딩.

Seung__ 2021. 8. 12. 20:50

1. 지연로딩


  • LAZY FETCH를 사용해서 프록시로 조회.
  • @Entity
    public class Member extends BaseEntity{
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "TEAM_ID")
        private Team team;
  • Member member = new Member();
    member.setUserName("hello");
    em.persist(member);
    
    em.flush();
    em.clear();
    
    Member m = em.find(Member.class, member.getId());
    
    
    tx.commit();
  • 로그를 보면 SELECT 쿼리를 통해 member만 읽어온다.
  • Hibernate: 
        select
            member0_.MEMBER_ID as member_i1_4_0_,
            member0_.createdBy as createdb2_4_0_,
            member0_.createdDate as createdd3_4_0_,
            member0_.lastModifiedBy as lastmodi4_4_0_,
            member0_.lastModifiedDate as lastmodi5_4_0_,
            member0_.modifiedBy as modified6_4_0_,
            member0_.LOCKIER_ID as lockier_8_4_0_,
            member0_.TEAM_ID as team_id9_4_0_,
            member0_.USERNAME as username7_4_0_,
            locker1_.id as id1_3_1_,
            locker1_.name as name2_3_1_ 
        from
            Member member0_ 
        left outer join
            Locker locker1_ 
                on member0_.LOCKIER_ID=locker1_.id 
        where
            member0_.MEMBER_ID=?
  • 이 상태에서 member.getTeam.getClass를 하면?
  • Team team = new Team();
    team.setName("teamA");
    em.persist(team);
    
    Member member = new Member();
    member.setUserName("hello");
    member.setTeam(team);
    em.persist(member);
    
    em.flush();
    em.clear();
    
    Member m = em.find(Member.class, member.getId());
    System.out.println("m = " + m.getTeam().getClass());
    
    tx.commit();
  • Select 쿼리를 통해 member만 가져오고
  • member.getTeam을 통해 가져온 객체는 프록시이다.
  • Hibernate: 
        select
            member0_.MEMBER_ID as member_i1_4_0_,
            member0_.createdBy as createdb2_4_0_,
            member0_.createdDate as createdd3_4_0_,
            member0_.lastModifiedBy as lastmodi4_4_0_,
            member0_.lastModifiedDate as lastmodi5_4_0_,
            member0_.modifiedBy as modified6_4_0_,
            member0_.LOCKIER_ID as lockier_8_4_0_,
            member0_.TEAM_ID as team_id9_4_0_,
            member0_.USERNAME as username7_4_0_,
            locker1_.id as id1_3_1_,
            locker1_.name as name2_3_1_ 
        from
            Member member0_ 
        left outer join
            Locker locker1_ 
                on member0_.LOCKIER_ID=locker1_.id 
        where
            member0_.MEMBER_ID=?
            
    ============================
    m = class hellojpa.Team$HibernateProxy$Asb0JG8r
    ============================
  • 즉 LAZY fetch를 적용하면 Member를 불러올 때 Team은 프록시로 불러온다.
  • 이 다음에 team에 있는 name이나 다른 애들을 불러올 때,
  • 예를들어 team.getName을 할 때 쿼리가 날라간다.
  • Team team = new Team();
    team.setName("teamA");
    em.persist(team);
    
    Member member = new Member();
    member.setUserName("hello");
    member.setTeam(team);
    em.persist(member);
    
    em.flush();
    em.clear();
    
    Member m = em.find(Member.class, member.getId());
    System.out.println("============================");
    System.out.println("m = " + m.getTeam().getClass());
    System.out.println("============================");
    
    System.out.println("============================");
    m.getTeam().getName();
    System.out.println("============================");
    
    tx.commit();
  • ============================
    m = class hellojpa.Team$HibernateProxy$w0kqwL1y
    ============================
    ============================
    Hibernate: 
        select
            team0_.TEAM_ID as team_id1_8_0_,
            team0_.createdBy as createdb2_8_0_,
            team0_.createdDate as createdd3_8_0_,
            team0_.lastModifiedBy as lastmodi4_8_0_,
            team0_.lastModifiedDate as lastmodi5_8_0_,
            team0_.modifiedBy as modified6_8_0_,
            team0_.name as name7_8_0_ 
        from
            Team team0_ 
        where
            team0_.TEAM_ID=?
    
    ============================
  • team.getName()을 호출하는 시점에 Team을 조회하는 쿼리가 나온다.

 

 

 

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


 

 

 

3. EAGER Fetch


  • Member 조회시 항상 Team 조회가 필요한 경우.
  • EAGER, 즉시로딩.

  • EAGER
@Entity
public class Member extends BaseEntity{

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
  • 쿼리는 두 번 날라갈까? 아님 조인할까?
Hibernate: 
    select
        member0_.MEMBER_ID as member_i1_4_0_,
        member0_.createdBy as createdb2_4_0_,
        member0_.createdDate as createdd3_4_0_,
        member0_.lastModifiedBy as lastmodi4_4_0_,
        member0_.lastModifiedDate as lastmodi5_4_0_,
        member0_.modifiedBy as modified6_4_0_,
        member0_.LOCKIER_ID as lockier_8_4_0_,
        member0_.TEAM_ID as team_id9_4_0_,
        member0_.USERNAME as username7_4_0_,
        locker1_.id as id1_3_1_,
        locker1_.name as name2_3_1_,
        team2_.TEAM_ID as team_id1_8_2_,
        team2_.createdBy as createdb2_8_2_,
        team2_.createdDate as createdd3_8_2_,
        team2_.lastModifiedBy as lastmodi4_8_2_,
        team2_.lastModifiedDate as lastmodi5_8_2_,
        team2_.modifiedBy as modified6_8_2_,
        team2_.name as name7_8_2_ 
    from
        Member member0_ 
    left outer join
        Locker locker1_ 
            on member0_.LOCKIER_ID=locker1_.id 
    left outer join
        Team team2_ 
            on member0_.TEAM_ID=team2_.TEAM_ID 
    where
        member0_.MEMBER_ID=?
  • EAGER 타입일 경우 두 테이블을 조인해서 한 번에 가져온다.
  • 즉 즉시로딩 이기 때문에, 프록시를 가져올 필요가 없다.
  • ============================
    m = class hellojpa.Team
    ============================
  • JPA 구현체는 가능하면 조인을 사용해서 SQL 한번에 함께 조회.

 

 

3. 프록시와 즉시로딩 주의


  • 가급적 지연 로딩만 사용(특히 실무에서..ㅜ)
  • 즉시 로딩을 적용하면 예상치 못한 SQL 이 발생
  • 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.
    • EAGER로 세팅하고 JPQL을 사용하면 N+1이슈 발생 가능
    •   List<Member> memberList = em.createQuery("select m from Member m", Member.class).getResultList();
    • Hibernate: 
          /* select
              m 
          from
              Member m */ select
                  member0_.MEMBER_ID as member_i1_4_,
                  member0_.createdBy as createdb2_4_,
                  member0_.createdDate as createdd3_4_,
                  member0_.lastModifiedBy as lastmodi4_4_,
                  member0_.lastModifiedDate as lastmodi5_4_,
                  member0_.modifiedBy as modified6_4_,
                  member0_.LOCKIER_ID as lockier_8_4_,
                  member0_.TEAM_ID as team_id9_4_,
                  member0_.USERNAME as username7_4_ 
              from
                  Member member0_
      
      Hibernate: 
          select
              team0_.TEAM_ID as team_id1_8_0_,
              team0_.createdBy as createdb2_8_0_,
              team0_.createdDate as createdd3_8_0_,
              team0_.lastModifiedBy as lastmodi4_8_0_,
              team0_.lastModifiedDate as lastmodi5_8_0_,
              team0_.modifiedBy as modified6_8_0_,
              team0_.name as name7_8_0_ 
          from
              Team team0_ 
          where
              team0_.TEAM_ID=?
    • em.find()는 PK를 딱 지정해서 조회가 가능.
    • 하지만 JPQL은 쿼리문이 SQL로 번역됨. 따라서 위와 같은 경우는 Member만 가지고옴.
    • 그러면 Member를 가지고왔더니 EAGER네???? TEAM도 가져오자 이렇게됨. ㄷㄷ
    • 해결방법
      - 일단 모두 LAZY 타입
      - fetch Join 동적으로 원하는 대로 가져옴
       List<Member> memberList = em.createQuery("select m from Member m join fetch m.team", Member.class)
       .getResultList();​
    • Hibernate: 
          /* select
              m 
          from
              Member m 
          join
              fetch m.team */ select
                  member0_.MEMBER_ID as member_i1_4_0_,
                  team1_.TEAM_ID as team_id1_8_1_,
                  member0_.createdBy as createdb2_4_0_,
                  member0_.createdDate as createdd3_4_0_,
                  member0_.lastModifiedBy as lastmodi4_4_0_,
                  member0_.lastModifiedDate as lastmodi5_4_0_,
                  member0_.modifiedBy as modified6_4_0_,
                  member0_.LOCKIER_ID as lockier_8_4_0_,
                  member0_.TEAM_ID as team_id9_4_0_,
                  member0_.USERNAME as username7_4_0_,
                  team1_.createdBy as createdb2_8_1_,
                  team1_.createdDate as createdd3_8_1_,
                  team1_.lastModifiedBy as lastmodi4_8_1_,
                  team1_.lastModifiedDate as lastmodi5_8_1_,
                  team1_.modifiedBy as modified6_8_1_,
                  team1_.name as name7_8_1_ 
              from
                  Member member0_ 
              inner join
                  Team team1_ 
                      on member0_.TEAM_ID=team1_.TEAM_ID
  • @ManyToOne, @OneToOne은 기본이  EAGER 타입임.
    --> LAZY로 변경해서 사용하자.
  • @OneToMany, @ManyToMany는 기본이  LAZY

 

4. 지연 로딩 활용

  • 근데 실무세어는 무조건 lazy ㅋㅋㅋㅋ
  • Member와 Team이 자주 함께 사용된다? -> 즉시 로딩
  • Member와 Team은 가끔 사용 -> 지연 로딩
  • Order와 Product는 자주 함께 사용 -> 즉시 로딩

 

5. 지연로딩 활용   - 실무


  • 모든 연관관계에 지연로딩 사용!
  • 실무에서 즉시 로딩 쓰지마
  • JPQL에서 fetch join이나, entity graph 기능을 사용해
  • 즉시 로딩은 상상치 못한 쿼리가 나간다. ㄷㄷㄷ

 

5. GitHub : 210812 LAZY FETCH


 

GitHub - bsh6463/JPA

Contribute to bsh6463/JPA development by creating an account on GitHub.

github.com

 

'인프런 > [인프런] 자바ORM 표준 JPA 프로그래밍' 카테고리의 다른 글

[예제] 연관관계 관리  (0) 2021.08.12
영속성 전이(CASCADE),고아 객체  (0) 2021.08.12
프록시  (0) 2021.08.12
[예제] 상속관계 매핑  (0) 2021.08.11
@MappedSuperclass  (0) 2021.08.11
Comments