BE전문가 프로젝트

즉시 로딩과 지연 로딩 본문

JPA

즉시 로딩과 지연 로딩

원호보고서 2022. 10. 31. 00:05

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

 

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

@Entity
public class Member extends BaseEntity{

    @Id
    @GeneratedValue
    private Long id;

    @Column(name="USERNAME")
    private String username;

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

    @OneToOne
    @JoinColumn(name="LOCKER_ID")
    private Locker locker;
}

FetchType.Lazy를 설정해준다면 해당 엔티티는 Proxy객체를 가져온다. 따라서 memberClass만 DB에서 조회하고 실제 team을 호출했을 때만 team을 Proxy객체로 가져온다는 의미이다.

public class JPAMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();                                     //트랜잭션 시작

        try {
            Team team = new Team();
            team.setName("A");
            em.persist(team);

            Member member = new Member();
            member.setCreatedBy("kim");
            member.setTeam(team);

            em.persist(member);

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

            Member foundMember = em.getReference(Member.class, member.getId());

            System.out.println("foundMember's team = " + foundMember.getTeam().getClass());

            tx.commit();
        }catch (Exception e){
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();
    }
}

위에 코드를 실행시켜 보면 foundMember 자체는 member Class라는 것을 알 수 있으며, foundMember의 Team은 실제 사용되는 시점에 Proxy 객체를 가져온다는 것을 알 수 있다. 따라서 Lazy 즉 지연로딩으로 조회하면 Proxy로 가져온다.

로직 특성상 member만 90%이상 사용하며 10%만 team을 같이 가져오는 구조라면 지연로딩을 사용하는 것이 효과적이다

 

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

@Entity
public class Member extends BaseEntity{

    @Id
    @GeneratedValue
    private Long id;

    @Column(name="USERNAME")
    private String username;

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

    @OneToOne
    @JoinColumn(name="LOCKER_ID")
    private Locker locker;
}

다시 위에 로직을 실행시켜보면 Lazy와 반대로  Proxy가 아닌 team과 member 모두 실제 값을 가져온다는 것을 알 수 있다

 

프록시와 즉시로딩 주의

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

우선 모든 연관관계는 LAZY로 설정하고 사용하는 것이 좋다. 하지만  fetchJoin을 통해 특정 상황에서 같이 가져오는 방법이 있음

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

 

지연 로딩 활용(이론적인 것)

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

하지만 실무에서는 지연로딩으로 설계하는 것이 중요하다. 즉시 로딩은 상상하지 못한 쿼리가 나간다.

'JPA' 카테고리의 다른 글

영속성 전이 :CASCADE  (0) 2022.10.31
영속성 전이 :CASCADE  (0) 2022.10.31
Proxy(프록시)  (0) 2022.10.30
쇼핑몰 만들기 3 - 상속관계 매핑  (0) 2022.10.30
@MappedSuperclass  (0) 2022.10.30
Comments