Spring/Spring Data JPA

[자바 ORM 표준 JPA 프로그래밍] 8. 프록시와 연관관계 관리

noahkim_ 2023. 12. 28. 16:02

김영한 님의 "자바 ORM 표준 JPA 프로그래밍" 책을 정리한 포스팅 입니다.

 

1. 프록시

  • 엔티티를 조회할 때, 연관된 엔티티를 항상 사용하는 것은 아닙니다.
  • 모든 연관관계를 한꺼번에 가져오게 될 경우, 성능이 떨어질 수 있습니다.

 

지연 로딩

  • 엔티티가 실제 사용될 때, 데이터베이스 조회를 하는 방법입니다.

 

프록시 객체

  • 실제 엔티티 객체 대신에 가짜 엔티티 객체를 사용합니다. (DB 접근 위임)
  • 실제 클래스를 상속받아 만들어짐 (겉모양 같음)
  • 실제 객체의 참조를 보관하며, 프록시 객체의 메소드를 호출하면 실제 객체의 메소드를 호출 (데이터베이스 조회 지연 목적)

 

em.getReference()
Member member = em.getReference(Member.class, "id1");
member.getName();
  • 프록시 객체 반환 + 엔티티 객체 생성 X (DB 조회 X)
  • (엔티티가 영속성 컨텍스트에 존재 시) 실제 엔티티 반환

 

초기화 과정

class MemberProxy extends Member {
	Member target = null;
    
    public String getName() {
    	if (target == null) {
            // 2. 초기화 요청
            // 3. DB 조회
            // 4. 실제 엔티티 생성 및 참조 보관
            this.target = ...;
        }
        
        return target.getName();
    }
}
  • 프록시 객체에 member.getName()을 호출하여 실제 데이터를 조회합니다.
  • 프록시 객체는 실제 엔티티가 생성되어 있지 않으면, 영속성 컨텍스트에 실제 엔티티 생성을 요청합니다.
  • 영속성 컨텍스트는 데이터베이스를 조회해서 실제 엔티티 객체를 생성합니다.
  • 프록시 객체는 생성된 실제 엔티티 객체의 참조를 멤버변수에 저장합니다.

 

특징
  • 처음 사용할 때 한번만 초기화됩니다.
  • 영속성 컨텍스트에 의해 초기화가 수행됩니다.
  • 초기화 작업을 통해 실제 객체에 접근이 가능해집니다.

 

LazyInitializationException
  • 준영속 상태에서 초기화 요청 시

 

프록시와 식별자

Member member = em.getRefreence(Member.class, "id1");
member.getName();
  • 식별자 값 보관 (파라미터로 식별자 값 전달됨)
  • 식별자 값 접근 가능 (프록시 초기화 X)
  • 유용) 엔티티 참조만 필요할 경우 (상세 데이터 필요 X) 

 

프록시 확인

PersistenceUnitUtil().isLoaded()
boolean isLoad = em.getEntityManagerFactory().getPersistenceUnitUtil().isLad(entity);
  • 프록시 인스턴스의 초기화 여부 확인

 

2. 즉시 로딩과 지연 로딩

즉시 로딩 (FetchType.EAGER)

  • 엔티티를 조회할 때 연관된 엔티티도 함께 조회합니다.

 

NULL 제약조건과 조인 전략
  • 기본적으로 즉시 로딩 시, 외부 조인을 사용합니다. (외래키 속성이 nullable 일 수 있음)
  • 외래키에 NOT NULL 설정을 할 경우, 내부 조인으로 쿼리를 작성합니다. (@JoinColumn(nullable=false))

 

지연 로딩 (FetchType.LAZY)

  • 연관된 엔티티를 프록시로 조회합니다.
  • 실제 데이터가 필요한 순간이 되어서야 데이터베이스 조회 (프록시 객체 초기화 O)

 

3. 지연 로딩 활용

컬렉션에 FetchType.EAGER 사용 시 주의점

컬렉션을 두 개 이상 즉시 로딩 X
  • 성능 저하

 

컬렉션 즉시 로딩 항상 외부 조인 O
  • 외래키가 null일 경우 아무런 결과 안나올 수 있음

 

4. 영속성 전이 : CASCADE

  • 특정 엔티티를 영속 상태로 만들 때, 연관된 엔티티도 함께 영속 상태로 만들 경우 사용합니다. (외래키 제약조건 기반)
  • 엔티티를 저장할 때, 연관된 모든 엔티티는 영속 상태여야 합니다.
  • 엔티티를 영속화할 때, 연관된 엔티티도 같이 영속화하는 편리함을 제공합니다.
  • CASCADE 옵션을 사용합니다.

 

저장

CascadeType.PERSIST
@Entity
public class Parent {
	...

	@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
	private List<Child> children = new ArrayList<Child>();
}
  • 부모를 영속화할 때, 연관된 자식들도 함께 영속화합니다.

 

삭제

CascadeType.REMOVE
@OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
  • 부모와 자식 엔티티 모두 제거하는 쿼리를 생성합니다.

 

고아 객체

@Entity
public class Parent {
    @Id @GeneratedValue
    private Long id;
    
    @OneToMany(mappedBy = "parent", orphanRemoval = true)
    private List<Child> children = new ArrayList<Child>();
    ...
}
  • 부모 엔티티와 연관관계가 끊긴 자식 엔티티를 자동으로 삭제할 수 있습니다.
  • @OneToMany에서만 사용할 수 있습니다.