JPA에서 프록시와 연관관계 관리는 엔티티와 데이터베이스 간의 관계를 효율적으로 처리하기 위해 중요한 역할을 한다. 특히, 프록시 객체를 통해 엔티티를 필요할 때만 로딩하고, 즉시 로딩과 지연 로딩을 활용하여 성능을 최적화할 수 있다. 또한, 영속성 전이(cascade)와 고아 객체 관리는 엔티티의 생명 주기를 관리하는 데 중요한 개념이다.
1. 프록시
- 프록시(Proxy)는 실제 엔티티 객체를 대신해 간접적으로 객체를 참조하는 객체이다. JPA는 데이터베이스의 데이터를 완전히 로딩하지 않고도 프록시 객체를 통해 지연 로딩을 사용할 수 있도록 지원한다. 프록시 객체는 실제 데이터가 필요한 시점에 데이터베이스에서 조회를 수행하여 데이터를 가져온다.
프록시 사용 예시
Member member = em.getReference(Member.class, memberId); // 프록시 객체 반환
위 코드에서 getReference() 메서드는 실제 Member 객체가 아닌 프록시 객체를 반환한다. 이 프록시 객체는 Member 엔티티의 데이터베이스 조회를 지연하고, 실제 데이터가 필요할 때만 데이터베이스에서 조회를 실행한다.
프록시의 특징
- 초기화되지 않은 상태: 프록시 객체는 초기화되지 않은 상태로 생성된다. 실제 데이터는 필요할 때 조회된다.
- 초기화 시점: 프록시 객체에 처음으로 접근하는 시점에 실제 데이터베이스 조회가 발생한다.
- 프록시 확인 방법: entity instanceof HibernateProxy 또는 PersistenceUnitUtil.isLoaded(entity) 메서드를 사용해 프록시인지 확인할 수 있다.
프록시 예시 코드
Member member = em.getReference(Member.class, memberId); // 프록시 객체
System.out.println(member.getClass()); // 프록시 클래스 확인
member.getName(); // 프록시 초기화(DB 조회)
2. 즉시 로딩(Eager Loading)과 지연 로딩(Lazy Loading)
즉시 로딩과 지연 로딩은 JPA에서 연관된 엔티티를 언제 로딩할지 결정하는 중요한 전략이다. 두 방식은 성능에 큰 영향을 미치므로 적절한 상황에서 선택적으로 사용해야 한다.
즉시 로딩(Eager Loading)
- 즉시 로딩은 엔티티를 조회할 때 연관된 모든 엔티티도 함께 조회하는 방식이다.
- @ManyToOne(fetch = FetchType.EAGER)와 같은 설정으로 즉시 로딩을 지정할 수 있다.
- 성능에 부정적 영향을 줄 수 있으며, 특히 연관된 엔티티가 많거나 깊은 경우 비효율적이다.
지연 로딩(Lazy Loading)
- 지연 로딩은 실제로 연관된 엔티티가 필요할 때 데이터베이스 조회를 실행하여 엔티티를 로딩하는 방식이다.
- @ManyToOne(fetch = FetchType.LAZY)와 같이 설정하면 지연 로딩을 사용할 수 있다.
- 성능 최적화에 유리하며, 필요할 때만 연관된 엔티티를 조회하므로 데이터베이스 접근을 최소화할 수 있다.
3. 지연 로딩 활용
- 지연 로딩(Lazy Loading)은 필요할 때만 데이터를 조회하므로, 불필요한 데이터 로딩을 줄여 성능을 최적화할 수 있다.
지연 로딩의 장점
- 성능 최적화: 필요할 때만 데이터를 조회하여 데이터베이스 부담을 줄인다.
- 메모리 효율성: 필요한 데이터를 즉시 조회하지 않으므로 메모리 사용을 최소화할 수 있다.
지연 로딩 사용 시 주의사항
- 프록시 초기화 시점: 지연 로딩을 사용하는 객체는 프록시 객체로 반환되며, 초기화되지 않은 프록시 객체에 접근할 경우 데이터베이스 조회가 발생한다.
- 지연 로딩과 트랜잭션: 지연 로딩은 트랜잭션 내에서 사용해야 한다. 트랜잭션이 종료된 상태에서 지연 로딩이 발생하면 LazyInitializationException 예외가 발생한다.
지연 로딩을 활용한 예시
@Entity
public class Member {
@ManyToOne(fetch = FetchType.LAZY)
private Team team; // 지연 로딩 설정
}
4. 영속성 전이(Cascade)
- 영속성 전이(Cascade)는 특정 엔티티의 상태 변화가 연관된 엔티티에 전파되는 기능이다. 예를 들어, 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장하거나 삭제할 때 자식 엔티티도 함께 삭제되는 것이 가능해진다.
Cascade 종류
- ALL: 모든 상태 변화를 전이한다.
- PERSIST: 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장한다.
- REMOVE: 부모 엔티티를 삭제할 때 자식 엔티티도 함께 삭제한다.
- MERGE: 부모 엔티티와 자식 엔티티를 병합한다.
- REFRESH: 부모 엔티티와 자식 엔티티를 새로고침한다.
- DETACH: 부모 엔티티와 자식 엔티티를 영속성 컨텍스트에서 분리한다.
Cascade 사용 예시
@Entity
public class Parent {
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL) // 모든 상태 변화를 자식에게 전이
private List<Child> children = new ArrayList<>();
위 예시에서 부모 엔티티(Parent)의 상태 변화는 모든 자식 엔티티(Child)에게도 전이된다. 따라서 부모를 저장하면 자식도 함께 저장되고, 부모를 삭제하면 자식도 함께 삭제된다.
5. 고아 객체(Orphan Removal)
고아 객체는 부모 엔티티와의 관계가 끊어진 자식 엔티티를 의미한다. 예를 들어, 부모-자식 관계에서 부모가 자식을 더 이상 참조하지 않게 되면 자식 엔티티는 고아 객체가 된다. JPA는 고아 객체 제거 기능을 통해 부모-자식 관계가 끊어진 자식 엔티티를 자동으로 삭제할 수 있다.
고아 객체 설정 예시
@Entity
public class Parent {
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true) // 고아 객체 제거
private List<Child> children = new ArrayList<>();
}
고아 객체 제거 특징
- orphanRemoval = true: 고아 객체가 된 자식 엔티티는 자동으로 삭제된다.
- 부모-자식 관계에서만 사용된다. 다른 엔티티와의 관계가 남아 있다면 고아 객체로 간주되지 않는다.
- 자식 엔티티가 부모의 컬렉션에서 제거될 때 해당 자식 엔티티는 고아 객체로 간주되어 삭제된다.
6. 영속성 전이 + 고아 객체, 생명 주기
영속성 전이와 고아 객체 제거는 함께 사용하여 부모-자식 관계에서 완전한 생명 주기 관리가 가능하다. 예를 들어, 부모 엔티티가 생성될 때 자식 엔티티도 함께 생성되고, 부모 엔티티가 삭제될 때 자식 엔티티도 함께 삭제되는 생명 주기 관리가 가능하다.
예시: 영속성 전이와 고아 객체 제거
@Entity
public class Parent {
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> children = new ArrayList<>();
public void addChild(Child child) {
children.add(child);
child.setParent(this);
}
public void removeChild(Child child) {
children.remove(child);
child.setParent(null); // 고아 객체로 처리
}
}
- CascadeType.ALL과 orphanRemoval = true를 함께 설정하여 부모 엔티티가 자식 엔티티의 생명 주기를 완전히 관리할 수 있다.
- addChild()와 removeChild() 메서드를 통해 부모-자식 관계를 관리하고, 부모-자식 관계가 끊어질 때 자식 엔티티는 자동으로 삭제된다.
영속성 전이와 고아 객체 제거의 특징
- 부모 엔티티와 자식 엔티티의 생명 주기를 일관되게 관리할 수 있다.
- 부모 엔티티가 삭제되면 자식 엔티티도 함께 삭제되어 데이터의 일관성을 유지할 수 있다.
- 연관된 엔티티 간의 관계가 끊어지면 자동으로 고아 객체가 삭제되므로 메모리와 데이터베이스 리소스를 절약할 수 있다.
- 프록시: JPA는 실제 데이터를 로딩하지 않고도 프록시 객체를 반환하여 지연 로딩을 통해 성능을 최적화한다.
- 즉시 로딩과 지연 로딩: 즉시 로딩은 연관된 모든 엔티티를 즉시 조회하는 방식이고, 지연 로딩은 실제로 데이터가 필요할 때만 조회하는 방식이다. 지연 로딩을 통해 성능을 최적화할 수 있다.
- 영속성 전이(Cascade): 부모 엔티티의 상태 변화를 자식 엔티티로 전파하여 엔티티 간의 생명 주기를 일관되게 관리할 수 있다.
- 고아 객체: 부모-자식 관계가 끊어지면 자동으로 삭제되며, 부모 엔티티에서 자식 엔티티의 메모리와 데이터베이스 리소스를 절약할 수 있다.
- 영속성 전이 + 고아 객체: 두 개념을 함께 사용하여 부모-자식 관계의 완전한 생명 주기 관리가 가능하다.
'Backend study > JPA' 카테고리의 다른 글
어노테이션 정리 (0) | 2024.11.01 |
---|---|
값 타입 (0) | 2024.11.01 |
고급 매핑 (0) | 2024.10.29 |
다양한 연관관계 매핑 (0) | 2024.10.25 |
연관관계 매핑 기초 (1) | 2024.10.22 |