✏️영속성 전이(Cascade)와 고아 객체 제거(orphanRemoval) 두 옵션에 대해 공부하면서 이 두 옵션이 실제로 어떻게 차이가 나는지 구체적으로 알게 되었다. 이에 대한 이해를 바탕으로 더 깊이 알아보고자 Cascade의 사용 위치, 조건, 옵션 종류, 그리고 orphanRemoval과의 차이점을 예시 코드와 함께 정리해보고자 한다.
영속성 전이는 객체 간의 관계에서 부모 엔티티의 상태 변화를 자식 엔티티에 전이하는 메커니즘이다. 이는 JPA에서 제공하는 기능으로, 특정 엔티티에 대한 작업이 연관된 다른 엔티티에도 자동으로 적용되도록 한다.
주문(Order)과 주문 항목(OrderItem)의 관계에서는 주문 쪽에 설정할 수 있다. 영속성 전이의 옵션 종류는 다음과 같다:
public enum CascadeType {
ALL, // Cascade all operations
PERSIST, // Cascade persist operation
MERGE, // Cascade merge operation
REMOVE, // Cascade remove operation
REFRESH, // Cascade refresh operation
DETACH // Cascade detach operation
}
고아 객체 제거는 부모 엔티티에서 사용되며, 일반적으로 @OneToMany 또는 @OneToOne 관계에서 사용된다.
이는 Cascade.REMOVE와 유사하게 삭제를 전파하는 기능이다.
부모 객체에서 리스트 요소를 삭제하면 해당 자식 객체는 매핑 정보가 없어지므로 자동으로 삭제된다.
고아 객체 제거는 참조가 제거된 엔티티를 자동으로 삭제하므로, 해당 엔티티가 다른 곳에서 참조되지 않는 것이 보장되어야 한다.
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "order", cascade = {CascadeType.ALL}, orphanRemoval = true)
private List<OrderItem> items = new ArrayList<>();
// getters, setters, etc.
}
@Entity
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
// getters, setters, etc.
}
// 영속성 전이와 고아 객체 제거 예시
Order order = new Order();
OrderItem item1 = new OrderItem();
OrderItem item2 = new OrderItem();
order.getItems().add(item1);
order.getItems().add(item2);
// 주문 저장 (Cascade.PERSIST)
entityManager.persist(order);
// 주문 항목 삭제
order.getItems().remove(item1); // item1이 삭제됨 (orphanRemoval=true)
Cascade.REMOVE는 부모 엔티티를 em.remove로 직접 삭제할 때 자식 엔티티가 삭제되는 반면, orphanRemoval은 부모 엔티티의 컬렉션에서 자식 엔티티를 제거하거나 참조를 null로 설정할 때도 자식 엔티티가 자동으로 삭제되는 더 강력한 기능을 제공한다. 이러한 차이는 도메인 모델의 일관성을 유지하는데 매우 중요한 역할을 한다.
위와 같은 옵션의 특징을 고려해봤을 때 부모-자식 관계에서 orphanRemoval=true + Cascade.ALL 조합의 사용이 권장된다고 할 수 있다. 이는 다음과 같은 명확한 이점들을 제공하기 때문이다:
완전한 생명주기 통합
코드 품질 향상
데이터 일관성 보장
성능 최적화
✏️결론을 내자면, 부모-자식 엔티티 관계에서orphanRemoval=true + Cascade.ALL조합은 매우 강력하고 유용한 도구가 된다.다만 이 조합을 사용할 때는 두 가지를 고려해야 한다. 첫째, 자식 엔티티가 다른 엔티티와 공유되지 않아야 하며, 둘째, 부모 엔티티의 생명주기에 자식 엔티티가 종속되어도 괜찮은 경우여야 한다.
이러한 조건이 충족된다면, 이 조합은 코드의 간결성, 데이터 정합성, 그리고 성능 최적화까지 모두 달성할 수 있는 효과적인 방법이 될 것이다. 특히 복잡한 도메인 로직에서 엔티티 간의 관계를 명확하게 표현하고 관리해야 할 때, 이 조합의 사용을 적극적으로 고려해볼 만할 것이다.