본문 바로가기

Spring/JPA

자바 ORM 표준 JPA 프로그래밍 #5(프록시와 연관관계)

들어가며

엔티티를 조회할 때 연관관계에 있는 Entity까지 조회를 하는 것은 효과적이지 않다. JPA는 이러한 상황을 위해 지연 로딩을 지원하는데 연관관계에 있는 엔티티에 프록시 객체를 집어 놓았다가 실제로 사용시 엔티티를 집어 넣는다.

 

프록시 

먼저 프록시에 대해서 살펴보도록 하자.

 

엔티티를 조회할 때 항상 연관된 엔티티를 사용하지는 않는다. 예를들어 지연로딩을 설정한 경우 엔티티 조회시 연관 엔티티에는 프록시 객체가 들어있다. 연관 엔티티를 상속받은 프록시 객체를 통해 데이터베이스에 쿼리를 해당 엔티티를 사용시에만 날릴 수 있게 한다.

프록시 초기화 과정

1. 프록시 객체에 접근을 시도하게 되면 실제 데이터를 조회한다.  
2. 프록시 객체가 초기화 되어있지 않으면 영속성 컨텍스트에 실제 엔티티 생성을 요청해 프록시 객체를 초기화 한다.
3. 영속성 컨텍스트는 데이터 베이스에 요청해 실제 엔티티 생성을 한다.
4. 프록시 객체는 이 엔티티를 프록시 객체 안에 저장한다.
5. getName()메소드를 호출해 해당 엔티티를 반환한다.

 

프록시 객체 특징

  • 프록시 객체는 처음 사용할 때 한 번만 초기화 된다.
  • 프록시 객체를 초기화 한다고 프록시 객체가 실제 엔티티로 비뀌지 않는다.
  • 프록시 객체는 원본 엔티티를 상속받은 객체이므로 타입 체크시 주의해야 한다.
  • 영속성 컨텍스트에 찾는 객체가 존재하면 em.getRefercne()시 실제 객체가 담긴다.
  • 초기화는 영속석 컨텍스트의 도움을 받아야 가능하다.

지연로딩과 즉시로딩

위에서 설명했듯이 JPA 기본 페치 전략에는 LAZY, EAGER가 있다.

LAZY는 연관 엔티티를 사용할 때 데이터 베이스에 쿼리를 날려 엔티티를 얻어오는 것이고, EAGER는 연관 엔티티를 사용하지 않더라도 연관 엔티티를 조회한다.

 

EAGER를 사용하게 된다면 N개의 엔티티를 조회하는 쿼리를 날렸을 때, N개의 엔티티와 연관관계가 있는 M개의 엔티티까지 전부 조회가 되므로 총 N*M개의 엔티티가 조회가 된다. 결과적으로 성능 저하를 초래할 수 있다.

따라서 모든 연관관계에 LAZY를 사용하고 필요시에 EAGER 타입을 사용하는 것이 낫다.

영속성 전이와 고아객체

특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶으면 영속성 전이 기능을 사용하면 된다.

JPA는 CASCADE 옵션으로 영속성 전이를 제공한다. 

다시 말해 영속성 전이를 통해 하나의 엔티티 만을 저장해도 연관관계에 있는 엔티티 까지 저장이 된다는 의미이다.

CASACDE의 종류로는 다음과 같다.

  • ALL(모두적용)
  • PERSIST(영속)
  • MERGE(병합)
  • REMOVE(삭제)
  • REFRESH(REFRESH)
  • DETACH(DETACH)

또한 JPA는 상위 엔티티와 연관관계가 끊어진 엔티티를 자동으로 삭제하는 기능을 제공한다. 이를 고아 객체 제거라고 하는데, 상위 엔티티가 갖고있는 컬렉션에서 참조만 제거하면 하위 엔티티가 자동으로 삭제된다.

 

다음과 같은 코드로 엔티티 스스로 생명주기를 관리할 수 있게 할 수 있다.

@Entity
public class Parent {
	@Id
    @GeneratedValue(stratge = GenerationType.AUTO)
    private Long id;
    
    @OneToMany(mappedBy = "parent", casacde = CascadeType.ALL, orphanRemoval = true)
    private List<Child> childs = new ArrayList<>();
}