JPA/JPA 기본

28. 즉시로딩, 지연로딩

sdafdq 2023. 10. 28. 17:54

지연로딩 LAZY

사용방법

 

@Entity
public class Member {
    public Member() {
    }

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name="USERNAME", nullable = false)
    private String name;

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

    @OneToOne
    Locker locker;
}

연관관계에서, fetch = FetchType.LAZY 해 놓으면

프록시로, team을 프록시로 일단 가져온다.

 

Team team1 = new Team();
team1.setName("Team1");
em.persist(team1);

Member member1 = new Member();
member1.setName("member1");
member1.setTeam(team1);
em.persist(member1);

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

Member findedMember = em.find(Member.class, member1.getId());
System.out.println("findedMember.getTeam().getClass() = " + findedMember.getTeam().getClass());

tx.commit();

findedMember.getTeam().getClass() = class hellojpa.Team$HibernateProxy$chtRE5JI

 

LAZY 이름 그대로 지연 로딩이다.

 

team에 접근한 후에 그제서야 select해서 가져올 것이다.

 

이 케이스가 바로,

비즈니스 로직 상 Member를 쓸 때 Team을 사용할 수도, 안할 수도 있을 때 쓰는 경우 좋고,

 

반대로 Member를 쓰면 Team을 거의 짝꿍처럼 쓰는 경우는

EAGER

@Entity
public class Member {
    public Member() {
    }

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name="USERNAME", nullable = false)
    private String name;

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

    @OneToOne
    Locker locker;
}

 

Team team1 = new Team();
team1.setName("Team1");
em.persist(team1);

Member member1 = new Member();
member1.setName("member1");
member1.setTeam(team1);
em.persist(member1);

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

Member findedMember = em.find(Member.class, member1.getId());
System.out.println("findedMember.getTeam().getClass() = " + findedMember.getTeam().getClass());

tx.commit();

이렇게 해 보면 Member 조회 쿼리가 나갈 때 join해서 team도 바로 가져오고, 

그에 따라 team도 프록시로 가져올 이유가 없어서 team의 클래스는 프록시가 아닌 그냥 Team이다.

 

 

근데, 실무에서는 LAZY만 쓴다.

예상치 못한 쿼리가 날라간다고 한다.

나중에 JPQL 배울 때 배우겠지만,

Team team1 = new Team();
team1.setName("Team1");
em.persist(team1);

Member member1 = new Member();
member1.setName("member1");
member1.setTeam(team1);
em.persist(member1);

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

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

tx.commit();

이렇게 조회부분 그냥 쿼리로 날려봤더니.

Hibernate: 
    /* select
        m 
    from
        Member m */ select
            m1_0.MEMBER_ID,
            m1_0.locker_id,
            m1_0.USERNAME,
            m1_0.TEAM_ID 
        from
            Member m1_0
Hibernate: 
    select
        t1_0.TEAM_ID,
        t1_0.name 
    from
        Team t1_0 
    where
        t1_0.TEAM_ID=?

join쿼리로 안나간다.

그냥 Member 조회해보다 Team도 필요해서 팀도 쿼리 날린 것 같다.

 

물론,

Team team1 = new Team();
team1.setName("Team1");
em.persist(team1);

Member member1 = new Member();
member1.setName("member1");
member1.setTeam(team1);
em.persist(member1);

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

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

tx.commit();

이렇게 join fetch 해서 같이 가져오면 되긴 한다.

 

Hibernate: 
    /* select
        m 
    from
        Member m 
    join
        fetch m.team */ select
            m1_0.MEMBER_ID,
            m1_0.locker_id,
            m1_0.USERNAME,
            t1_0.TEAM_ID,
            t1_0.name 
        from
            Member m1_0 
        join
            Team t1_0 
                on t1_0.TEAM_ID=m1_0.TEAM_ID

그럼 이렇게 join 해서 나간다.

 

 

그래도 여튼 가급적이면 지연로딩만 사용하자.

 

 

근데,

@ManyToOne, @OneToOne 같이

XToOne은 즉시로딩이 기본이다.

그래서 우리가 이걸 명시적으로 fetch=FetchType.LAZY로 기본으로 설정 해 주어야 한다.

 

 

 

그럼 이제 어떻게 할까.

Member와 Team을 자주 함께 사용한다 -> 즉시 로딩

Member와 Order는 가끔 사용한다 -> 지연 로딩

Order와 Product는 자주 함께 사용한다 -> 즉시 로딩

....

이라고 하는데,

실무에서는 모두 지연로딩으로 하라고 한다.

 

저건 그냥 이론적인 거고.

 

 

 

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

30. 실전 연관관계 관리  (0) 2023.10.29
29. 영속성 전이와 고아 객체  (0) 2023.10.28
27. 프록시  (0) 2023.10.27
25. Mapped Superclass 매핑 정보 상속  (0) 2023.10.27
24. 상속관계 매핑  (0) 2023.10.26