JPA 프록시와 연관관계 관리

이상민·2021년 11월 17일
0

JPA

목록 보기
7/8
post-thumbnail

1. 프록시

JPA는 지연로딩 사용시, 연관관계 객체 호출 전까지는 프록시 객체로 되어있다. 지연 로딩을 통해 연관관계 객체를 사용하지 않을 시, 불필요한 데이터를 가져오지 않아 오버헤드를 줄인다.

// DB에 쿼리해 엔티티 객체를 반환한다
em.find();
// DB에 아직 조회하지 않은 가짜(프록시) 객체를 반환한다
// 엔티티가 이미 영속성 컨텍스트에서 관리되고 있다면, 실제 객체를 반환한다
em.getReference();

1-1. 프록시의 특징

  • 프록시 특징

    • 실제 클래스를 상속 받아서 만들어진다(CGLib)
    • 실제 클래스와 겉 모양이 같다
    • 사용 입장에서는 진짜 객체인지, 프록시 객체인지 구분하지 않고 사용할 수 있다
  • 프록시 객체는 실제 객체의 참조(target)를 보관한다

  • 1)프록시 객체의 메소드를 호출하면 프록시 객체는 2)초기화 요청을 통해 target을 가져오고, 3)실제 객체의 메소드를 호출한다

  • JPA의 표준스펙은 아니고, Hibernate에서 제공하는 기능이다

  • 프록시 객체가 초기화를 한다고 실제 엔티티로 바뀌지는 않는다

  • 한번 프록시로 조회를 하면, find() 메소드로 객체를 가져와도 프록시를 반환한다

  • 객체가 준영속 상태일 때, 프록시를 초기화하면 LazyInitializationException 예외가 발생한다

  • 프록시도 영속성 컨텍스트에 의해 관리(즉 1차 캐시에 존재)한다는 점을 잊지 말자

1-2. 프록시 유틸 메소드

  • 프록시 객체의 초기화 여부 확인
PersistenceUnitUtil.isLoaded(entity)
  • 프록시 클래스 확인 방법
entity.getClass().getName()
  • 프록시 강제 초기화
org.hibernate.Hibernate.initialize(entity)
  • 참고로 JPA 표준에 강제 초기화는 없다

2. 즉시 로딩과 지연 로딩

2-1. fetch 옵션

@ManyToOne(fetch = FetchType.LAZY)
  • 위 옵션을 사용해 지연 로딩을 설정할 수 있다. 지연 로딩을 설정하면, 프록시 객체를 반환한다

  • 한번에 가져오도록 옵션을 FetchType.EAGER으로 설정할 수도 있다. 설정 시 JPA 구현체는 가능하면 조인을 사용해 한 쿼리를 통해 조회한다

  • fetch 옵션의 기본 설정은 다음과 같다

// JPA 2.0 스펙
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER

2-2. 주의 사항

모든 연관관계에 지연 로딩을 사용해라!!

  • 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생할 수 있다. 많은 테이블을 참조한다면, 엔티티 객체를 하나 조회하는 것으로 인해 수많은 조인을 하는 쿼리를 발생시킨다.

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


3. 영속성 전이와 고아 객체

3-1. 영속성 전이

@OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST)
  • 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을때 사용한다

  • FK의 CASCADE와 헷갈리지 않도록 주의하자

3-2. 고아 객체

@OneToMany(mappedBy="parent", orphanRemoval=true)
  • 고아 : 부모 엔티티와 연관관계가 끊어진 자식 엔티티

  • orphanRemoval 옵션을 true로 설정하면, 고아 상태 객체는 삭제된다

  • 참조하는 곳이 하나일 때 사용해야한다

  • 영속성 전이, 고아 객체 삭제 설정을 모두 켜면, 부모를 통해서 자식의 라이프사이클을 관리할 수 있다

profile
편하게 읽기 좋은 단위의 포스트를 추구하는 개발자입니다

0개의 댓글