[JPA] 지연로딩과 즉시로딩 (JPA 기본편 by 김영한)

su_y2on·2022년 1월 29일
0

JPA

목록 보기
10/17
post-thumbnail

지연로딩과 즉시로딩

앞에서 익힌 프록시라는 개념을 떠올리며 지연로딩과 즉시로딩을 이해해보도록 하겠습니다 :)



예를들어 Team과 Member가 일대다로 연관관계를 갖고있을 때 Member를 조회할 때 마다 해당 Member의 Team도 조회해야할까요? 비즈니스적 니즈에 따라 그럴수도 아닐 수도 있습니다. 만약 그럴 필요가 없다면 엄청난 낭비겠죠?

이럴때 사용하는 게 지연로딩입니다. 실제로 Member를 통해 Team에 대한 정보를 읽어와야할 때까지 DB에서 조회를 미루는 것이죠 🤗 (어디서 많이 들어본 개념..)

따라서 아래와 같이 Member를 조회 할때는 Team객체에 프록시객체를 넣어오는 것입니다. 그리고 getTeam같이 실제로 team이 필요한 경우에 초기화가 이뤄지는 것이죠!


지연로딩으로 설정해주기 위해서는 아래와 같이 간단하게 fetch옵션을 LAZY로 설정해주면 됩니다.

public class Member {

    @ManyToOne(fetch = FetchType.LAZY) // 다대일(member관점) + 프록시객체로 조회
    @JoinColumn(name= "TEAM_ID") // FK는 뭔지
    private Team team;
    
    }



물론 반대의 경우 (즉시로딩 : Member와 Team을 자주 같이 조회해야하는 경우)에는 fetch옵션을 EAGER로 설정해주면 됩니다.

 @ManyToOne(fetch = FetchType.EAGER) 




실제 코드를 보면서 쿼리가 어떻게 날라가는지 보도록 하겠습니다.

Member member = new Member();
member.setUsername("a");

Team team = new Team();
team.setName("b");
member.setTeam(team);

em.persist(member);
em.persist(team);

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


Member findMember = em.find(Member.class, member.getId());
System.out.println("member = " + findMember.getClass()); // 멤버객체
System.out.println("team = " + findMember.getTeam().getClass()); // 프록시객체
System.out.println("team = " + findMember.getTeam().getName()); // 초기화하면서 select문날림


즉시로딩(EAGER)

JPA에서 최적화를 해주기때문에 join으로 team을 가져옵니다.

 select
        .....
 from
     Member member0_ 
 left outer join
     Team team2_ 
         on member0_.TEAM_ID=team2_.TEAM_ID 
 where
     member0_.MEMBER_ID=?



지연로딩(LAZY)

지연로딩시에는 join을 하지 않음을 알 수 있습니다.

select
        .....
from
   Member member0_ 
where
   member0_.MEMBER_ID=?

물론 JPA의 최적화로 인해 조인을 해서 큰 차이가 있나 하겠지만 JPQL을 사용하면 또 차이가 훨씬 크게 느껴집니다.





JPQL

JQPL을 사용해야하는 멤버 전체조회를 통해 하나의 멤버를 조회해 오도록 하겠습니다.

 Member member = new Member();
 member.setUsername("a");


Team team = new Team();
team.setName("b");
member.setTeam(team);



em.persist(member);
em.persist(team);



em.flush();
em.clear();
            
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
System.out.println(members);      
            



즉시로딩(EAGER)

JPQL은 전달한 쿼리 그대로 만들어서 나가기 때문에 JPA의 최적화를 거치지 않습니다. 따라서 전체조회를 위해 Member에 select문 한번, 즉시로딩을 위해 그 멤버의 team의 값을 가져오기 위해 select문 1번 총 2번의 쿼리가 날라갑니다.

지금은 member가 적지만.. 만약 멤버가 많아지고 모두 다른 team에 속해있다면 N + 1의 문제가 날 수 있습니다. N+1문제란 Member전체조회를 했을때 날리는 하나의 쿼리(select * from Member m) 때문에 N개의 추가 쿼리 N개가(각각 member의 team을 가져오는 select문) 발생하는 문제입니다.

Hibernate: 
 	select
        	....
 	from
        	Member member0_
       
Hibernate: 
    select
        team0_.TEAM_ID as team_id1_7_0_,
        team0_.name as name2_7_0_ 
    from
        Team team0_ 
    where
        team0_.TEAM_ID=?



지연로딩(LAZY)

지연로딩시에는 예상대로 한번의 쿼리만 나갑니다.

Hibernate: 
 	select
        	  ....
	 from
                  Member member0_






🔥결론🔥
가급적 모든 연관관계에 지연로딩을 default로 설정하고 정말 필요할 때만 즉시로딩으로 바꿔줘야 바람직합니다!!

0개의 댓글