ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 즉시로딩과 지연로딩
    JPA/JPA 기본 2023. 7. 11. 18:05

    지연로딩(LAZY)

    @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;
     }

    fetch = FetchType.LAZY는 지연로딩으로 설정한다는 뜻이다. 지연로딩을 사용한다면 em.find()를 했을 때 프록시로 가지고 온다.

     

    Team team = new Team();
    team.setName("teamA");
    em.persist(team);
    
    Member member1 new Member();
    member1.setUsername("member1");
    member1.setTeam(team);
    
    em.persist(member1);
    
    em.flush();
    em.clear();
    
    Member member = em.find(Member.class, member1.getId());
    
    member.getTeam().getName(); // 이 시점에 쿼리가 나가면서 초기화

    member 엔티티에서 team을 지연로딩으로 설정했기 때문에

    Member m = em.find(Member.class, member1.getId());

    위의 코드 줄에서 프록시가 호출이 된다.

     

    그리고 마지막 줄인 m.getTeam().getName()으로 team의 속성을 사용하게 될 때 쿼리가 나오면서 프록시 객체가 초기화, db에서 해당 값을 가져오는 것이다.

    -> team을 가져올 때가 아니라 실제 team을 사용하는 시점에 초기화(DB조회)

     

    비즈니스 로직 상 member를 조회했을 때 member만 사용하는 경우가 많을 때에는 지연로딩을 사용하는 것이 효율적이다.

     

     

    하지만 member와 team을 같이 사용해야하는 경우가 대부분이라면 즉시 로딩을 쓰는 것이 더 낫다.

     

     

     

     

    즉시로딩(EAGER)

    @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;
     }

    fetch = FetchType.EAGER로 바꿔줬을 때 똑같은 로직을 출력해본다면 처음 쿼리를 출력할 때 member와 team의 쿼리가 같이 호출된다. 그리고 team의 프록시가 아니라 실제의 객체가 호출된다.

     

     

     

    프록시와 즉시로딩 주의

    • 가급적 지연 로딩만 사용(특히 실무에서)
    • 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생
    • 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.
    • @ManyToOne, @OneToOne은 기본이 즉시 로딩 -> LAZY로 설정해야함
    • @OneToMany, @ManyToMany는 기본이 지연로딩

     

    테이블이 별로 없는 프로젝트에서는 최적화를 딱히 고민하지 않아도 상관없으니 아무 로딩이나 사용하면 되지만 실무에서는 테이블이 복잡해지면 예상치 못한 SQL이 너무 많이 생기기 때문에 무조건 지연 로딩만을 사용해야 한다.

     

     

     

     

    N+1 문제를 해결하는 방법

    가장 많이 쓰는 방법 - fetch join 방법 : join 쿼리를 날려 동적으로 원하는 테이블을 선택해서 가져옴

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

    LAZY 상태에서도 member랑 team을 한번에 쿼리를 날려 호출할 수 있다. 이럴 경우에는 loop를 돌려도 쿼리가 계속 나가지 않는다는 장점이 있다.

     

     

     

    이론적으로는 Member와 Team을 자주 함께 사용하면 즉시 로딩을 사용하는 것이 효율적이라고 하지만 실무에서는 무조건 지연 로딩을 써야한다는 것이 중요하다.

    'JPA > JPA 기본' 카테고리의 다른 글

    값 타입  (0) 2023.07.11
    영속성 전이(CASCADE)  (0) 2023.07.11
    프록시  (0) 2023.07.11
    상속관계 매핑  (0) 2023.07.11
    다양한 연관관계 매핑  (0) 2023.07.11
Designed by Tistory.