JPA - 프록시와 연관관계 정리

bp.chys·2020년 6월 8일
0

JPA

목록 보기
6/15

프록시 (proxy)

프록시 기초

  • entityManager.find() : 데이터베이스를 통해서 실제 엔티티 객체 조회
  • entitiyManager.getReference() : 데이터베이스 조회를 미루는 가짜 엔티티 객체 조회

프록시 특징

  • JPA 내부적으로 실제 엔티티 클래스를 상속 받아서 만들어진다.
  • 실제 클래스와 겉 모양이 같다.
  • 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 된다.
  • 프록시 객체는 실제 객체의 참조 대상(target)를 보관한다.
  • 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다.

프록시 객체의 초기화

  • 프록시 객체는 처음 사용할 때 한번만 초기화 된다.
  • 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것이 아니다. 초기화를 통해서 실제 엔티티에 접근이 가능해진 것이다.
  • 프록시 객체는 원본 엔티티를 상속 받는다. 따라서 타입 체크시 == 비교가 아닌, instead of 로 타입을 비교해야 한다.
  • 영속성 컨텍스트(1차 캐시)에 이미 찾는 엔티티가 존재하면, em.getReference()를 호출해도 실제 엔티티를 반환한다. (굳이 실객체가 있는제 프록시를 반환할 이유가 없다. 최적화 등의 이유로)
  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생한다.
    • em.close(), em.detach(entity) 를 하면 더이상 영속성 컨텍스트의 도움을 받을 수 없기 때문에 예외가 발생한다.
    • 하이버네이트는 org.hibernate.LazyInitializationException을 터뜨린다.
Member member = em.getReference(Member.class, "id1"); // 프록시 객체
member.getName(); // 실제 객체로 부터 값을 가져온다.

즉시 로딩과 지연 로딩

지연로딩

  • Member와 Team은 다대일 관계
  • Member 정보만 필요할 때는 Team 정보가 굳이 필요없는 경우도 있다.
  • fetch 속성을 달리 지정해 줌으로써 지연로딩을 할 수 있다.
  • @ManyToOne(fetch = FetchType.LAZY)

Team team = member.getTeam(); // 아직 까지 프록시
team.getName(); // 실제 team을 사용하는 시점에 초기화(DB조회)

즉시로딩

  • 만약 도메인에서 Member와 Team이 항상 함께 사용된다면 지연 로딩을 하는 것보다 즉시로딩을 할 수 도 있다.
  • @ManyToOne(fetch = FetchType.EAGER)
  • 그러나 즉시로딩은 JPQL에서 N+1문제가 발생하는 등 예상하지 못한 SQL이 발생할 수 있다.
  • 실무에서는 가급적 모든 연관관계에서 지연 로딩을 사용하는 것이 좋다.
    • @ManyToOne, @OneToOne은 기본이 즉시 로딩 👉 LAZY로 설정
    • @OneToMany, @ManyToMany는 기본이 지연 로딩

참고자료

  • 자바 ORM 표준 JPA 프로그래밍, 김영한 저
profile
하루에 한걸음씩, 꾸준히

0개의 댓글