[JPA #3] 즉시 로딩, 영속성 전이, 그리고 값 타입
[JPA #3] 즉시 로딩, 영속성 전이, 그리고 값 타입
주요 키워드
- 프록시
- 지연 로딩(Lazy loading)
- 즉시 로딩(Eager loading)
- 영속성 전이
- 고아 객체
- 임베디드 타입
- 값 타입
- 불변 객체
프록시
em.find() vs em.getReference()
em.find()
: 실제 DB에서 엔티티 조회em.getReference()
: 프록시 객체 반환 (조회 지연)
프록시 객체의 특징
- 실제 클래스를 상속받아 생성됨
- 내부적으로 실제 객체를 참조(target)
- 처음 사용할 때 한 번만 초기화됨
- 초기화 후에도 프록시 객체는 그대로 유지됨
==
대신instanceof
로 타입 비교 필요- 영속성 컨텍스트에 이미 존재하면 실제 엔티티 반환
- 준영속 상태에서 프록시 접근 시 오류 발생 가능 (LazyInitializationException)
프록시 관련 유틸
- 초기화 여부 확인:
PersistenceUnitUtil.isLoaded(entity)
- 프록시 클래스 확인:
entity.getClass().getName()
- 강제 초기화:
Hibernate.initialize(entity)
또는member.getName()
즉시 로딩과 지연 로딩
- 지연 로딩(LAZY): 프록시로 조회, 실제 사용 시점에 초기화
- 즉시 로딩(EAGER): 조회 시 항상 연관된 엔티티까지 함께 조인 수행
주의사항
- 모든 @~ToOne 관계에 지연 로딩 설정 권장 (특히 실무)
- 즉시 로딩은 예기치 못한 SQL/N+1 문제 발생 가능
- JPQL에서도 즉시 로딩은 문제 소지가 큼
@ManyToOne
,@OneToOne
기본은 즉시 로딩 → LAZY로 설정 권장@OneToMany
,@ManyToMany
는 기본이 지연 로딩
영속성 전이 (Cascade)
정의
- 부모 엔티티를 영속화하면 자식 엔티티도 함께 영속화되도록 설정
특징
- 연관관계 매핑과 무관
- 부모가 자식의 생명주기를 관리할 때만 사용
- 다른 곳에서 자식 엔티티를 공유한다면 사용 금지
고아 객체
- 연관관계가 끊어진 자식 엔티티를 자동 삭제
orphanRemoval = true
사용- CascadeType.REMOVE와 유사
- 참조하는 곳이 하나인 엔티티에만 사용
영속성 전이 + 고아 객체 = 생명주기 전이
em.persist()
/em.remove()
없이 부모가 자식의 생명주기 완전 관리 가능- DDD의 Aggregate Root 구현 시 유용
값 타입
정의
int
,String
같은 단순 타입 또는 임베디드 객체- 식별자가 없고, 변경 시 전체 교체 방식
값 타입 분류
- 기본 값 타입: int, double, String 등
- 임베디드 타입(복합 값 타입):
@Embeddable
클래스 - 값 타입 컬렉션:
@ElementCollection
임베디드 타입
- 같은 테이블에 매핑됨
- 객체와 테이블을 정밀하게 매핑 가능
- 중첩 임베디드 타입 가능
- 컬럼명이 중복될 경우
@AttributeOverride
로 재정의 필요 - 엔티티 간 값 타입 공유 금지 → 참조 공유 위험
- 객체는 복사해서 사용하거나 불변 객체로 설계
불변 객체
- 생성자만 제공하고 setter 미제공
- 값 변경 불가 → 사이드 이펙트 예방
- equals() 오버라이딩으로 동등성 비교
값 타입 컬렉션
- 값 타입을 여러 개 저장할 때 사용
- 별도 테이블 필요, 모든 컬럼을 묶어 PK 구성 필요
- 기본적으로 지연 로딩 사용
- 변경 시 전체 삭제 후 다시 저장 → 성능 이슈, 사용 주의
- cascade + orphanRemoval 기본 적용
대안
- 일대다 관계로 대체 + 영속성 전이 & 고아 객체 제거 조합
- ex) AddressEntity
값 타입은 진짜 값 타입일 때만 사용하고, 식별자가 필요하거나 변경 추적이 필요하면 엔티티로 설계해야 한다.
This post is licensed under CC BY 4.0 by the author.