JPA는 지연로딩 사용시, 연관관계 객체 호출 전까지는 프록시 객체로 되어있다. 지연 로딩을 통해 연관관계 객체를 사용하지 않을 시, 불필요한 데이터를 가져오지 않아 오버헤드를 줄인다.
// DB에 쿼리해 엔티티 객체를 반환한다
em.find();
// DB에 아직 조회하지 않은 가짜(프록시) 객체를 반환한다
// 엔티티가 이미 영속성 컨텍스트에서 관리되고 있다면, 실제 객체를 반환한다
em.getReference();
프록시 특징
프록시 객체는 실제 객체의 참조(target)를 보관한다
1)프록시 객체의 메소드를 호출하면 프록시 객체는 2)초기화 요청을 통해 target을 가져오고, 3)실제 객체의 메소드를 호출한다
JPA의 표준스펙은 아니고, Hibernate에서 제공하는 기능이다
프록시 객체가 초기화를 한다고 실제 엔티티로 바뀌지는 않는다
한번 프록시로 조회를 하면, find()
메소드로 객체를 가져와도 프록시를 반환한다
객체가 준영속 상태일 때, 프록시를 초기화하면 LazyInitializationException
예외가 발생한다
프록시도 영속성 컨텍스트에 의해 관리(즉 1차 캐시에 존재)한다는 점을 잊지 말자
PersistenceUnitUtil.isLoaded(entity)
entity.getClass().getName()
org.hibernate.Hibernate.initialize(entity)
@ManyToOne(fetch = FetchType.LAZY)
위 옵션을 사용해 지연 로딩을 설정할 수 있다. 지연 로딩을 설정하면, 프록시 객체를 반환한다
한번에 가져오도록 옵션을 FetchType.EAGER
으로 설정할 수도 있다. 설정 시 JPA 구현체는 가능하면 조인을 사용해 한 쿼리를 통해 조회한다
fetch 옵션의 기본 설정은 다음과 같다
// JPA 2.0 스펙
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
모든 연관관계에 지연 로딩을 사용해라!!
즉시 로딩을 적용하면 예상하지 못한 SQL이 발생할 수 있다. 많은 테이블을 참조한다면, 엔티티 객체를 하나 조회하는 것으로 인해 수많은 조인을 하는 쿼리를 발생시킨다.
즉시 로딩은 JPQL에서 N+1 문제를 일으킨다
@OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST)
특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을때 사용한다
FK의 CASCADE와 헷갈리지 않도록 주의하자
@OneToMany(mappedBy="parent", orphanRemoval=true)
고아 : 부모 엔티티와 연관관계가 끊어진 자식 엔티티
orphanRemoval 옵션을 true로 설정하면, 고아 상태 객체는 삭제된다
참조하는 곳이 하나일 때 사용해야한다
영속성 전이, 고아 객체 삭제 설정을 모두 켜면, 부모를 통해서 자식의 라이프사이클을 관리할 수 있다