본문 바로가기

개인적으로 공부한 것을 정리해 놓은 블로그입니다 틀린 것이 있으면 댓글 부탁 드립니다!


JPA

JPA 공부 9 - 즉시로딩과 지연로딩

반응형

 

지연 로딩 (Lazy loading)

 

연관된 내용을 프록시로 가져온다 . 

 

@Entity
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

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

 

연관된 필드의 연관관계 맵핑 옵션으로 fetch 타입을 지정할 수 있다.  Lazy (지연로딩), Eagar(즉시로딩)이 있다.  

 

 //팀 저장
Team team = new Team();
team.setName("team1");
em.persist(team);

//멤버 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);

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

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

//프록시 초기화 시점
System.out.println("----------초기화 시점-----------");
String teamName = member1.getTeam().getName();
System.out.println("---------------------");
System.out.println("teamName = " + teamName);
System.out.println("---------------------");

 

team에 필드에 대한 내용에 접근할 때 초기화가 되기는데 위에서는 클래스 정보에 접근하는 것이기 때문에 초기화는 되지 않는다. 

 

어떤 클래스가 출력될까?

 

 

지연로딩이 설정되어 있기 떄문에  프록시객체가 리턴되었다 . 

 

 

초기화 시점에 em.clear로 영속성 컨텍스트가 빈 상태이기 떄문에  쿼리가 날라가고  proxy객체가 참조하는 실제 엔티티를 통해 target.getName()이 호출되어 값이 리턴되었다 . 

 

 

즉시로딩 (EAGAR)

 

연관된 내용을 한번에 가져온다 .

 

@Entity
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne(fetch = FetchType.EAGAR)
    @JoinColumn(name = "team_id")
    private Team team;
}

 

 

 

 //팀 저장
Team team = new Team();
team.setName("team1");
em.persist(team);

//멤버 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);

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

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

String teamName = member1.getTeam().getName();
System.out.println("teamName = " + teamName);

 

 

결과

 

조인으로 한번에 다가져왔다 . 팀의 클래스도 진짜 객체가 들어간다.

 

 

 

프록시와 즉시로딩 주의점

 

- 가급적 지연 로딩만 사용하는 것이 좋다.

    (예상치 못한 SQL이 나온다.)

 

- 즉시로딩은 JPQL에서 N+1 문제를 일으킨다 . 

  -   em.find() 등의 JPA메서드는  이미 즉시로딩에 대해 최적화가 되어 있기 때문에 조인으로 한방에 데이터를 가져오지만 

       JPQL같은 경우에는 JPQL이 SQL로 변환되어서 날라가기 떄문에 그 안에 어떤 연관관계가 있는지는 JPA가 알 수가 없다 . 

       예시) "select member from  member m"  <-JPQL 

       예시의 JPQL처럼 member 는 팀과 연관관계가 맺어 있지만  쿼리가 날라갈떄는 "select * from member" 이렇게만 날라가기            때문에 어떤 연관관계를 갖고있는지는 알지 못한다 . 떄문에 member 데이터를 가지고오고나서 팀과의 연관관계를 보고 팀을 찾는        한번 더 날린다.  만약 연관관계가 4개다 하면 멤버를 가지고와서 4개 쿼리를 다시날리는 거다 . 이를 N+1 문제라고 한다.  

          *해결방안: 처음에 lazyLoding 으로 모두 설정하고  필요한경우 JPQL의 fetch조인으로 가져온다. 

 

- @ManyToOne , @OneToOne 은 기본이 즉시 로딩이다.  

 

 

반응형