부모와 자식 관계의 엔티티(예를 들어 게시판과 댓글)를 설계 후 연관관계 매핑을 해주었다.
그 후 자식 엔티티의 일부를 제거하는 코드를 작성하는데 연관 관계는 제거되었지만 자식 엔티티의 데이터가 DB에 그대로 남아있었다. 관련된 문제를 해결하는 방법을 적어보았다.
1. JPA와 연관관계
JPA는 엔티티간의 연관관계를 쉽게 관리할 수 있다.
CascadeType와 orphanRemoval 옵션을 통해 부모 엔티티와 자식 엔티티의 관계를 효율적으로 관리할 수 있게 된다.
이 글에선 두 옵션의 기능과 발생하는 문제점에 대해서 적어보았다.
2. CascadeType.REMOVE
CascadeType.REMOVE는 부모 엔티티가 삭제될 때 연관된 자식 엔티티도 함께 삭제되는 옵션이다.
예를 들어, 부모 엔티티가 자식 엔티티와 일대다 관계를 가지고 있다면, 다음과 같이 CascadeType.REMOVE를 설정할 수 있다.
@Entity
public class Parent {
@OneToMany(cascade = CascadeType.REMOVE)
private List<Child> children;
}
하지만 다음과 같은 상황에 CascadeType.REMOVE 만으로는 부모 엔티티와의 관계가 끊어진 자식 엔티티(고아 객체)가 DB에 남게 될 수 있다.
컬렉션에서 자식 엔티티가 제거되는 경우
부모 엔티티가 자식 엔티티를 포함하는 컬렉션(현재 예시에서는 List)을 가지고 있는 경우, 이 컬렉션에서 특정한 자식 엔티티를 제거하면 부모 엔티티와 자식 엔티티간의 연관관계가 해제된다. CascadeType.REMOVE 옵션은, 부모 엔티티가 제거될 때 자식 엔티티를 제거하는 옵션이므로 이 상황에서는 자식 엔티티가 참조하는 부모 객체가 없는 고아상태가 된다.
parent.getChildren().remove(child);
고아 객체는 참조하는 부모 객체가 없는 상태다. 따라서 orphanRemoval 옵션을 이용해야 한다.
3. orphanRemoval=true
orphanRemoval=true는 부모 엔티티와 연관된 자식 엔티티 중에 관계가 끊어진 자식 엔티티를 자동으로 삭제하는 옵션이다.
(CascadeType.REMOVE는 부모 엔티티가 삭제되면 자식 엔티티를 삭제하는 옵션이므로 별개로 작동한다.)
다음과 같이 사용한다.
@Entity
public class Parent {
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<Child> children;
}
만약 위의 경우처럼
parent.getChildren().remove(child);
자식 엔티티의 컬렉션을 가져와 일부를 제거하는 경우, 참조하는 부모 객체가 없어진 고아 객체를 orphanRemoval=true 옵션을 통해 제거할 수 있다.
4. 결론
결론적으로, CascadeType.REMOVE와 orphanRemoval=true 옵션을 함께 사용하면 엔티티 간의 관계를 더욱 철저하게 관리할 수 있다.
이를 통해 애플리케이션의 데이터 일관성을 유지하고 실수로 인한 데이터 불일치를 방지할 수 있게 된다.