JPA

JPA 특정 엔티티 삭제시 연관된 엔티티도 함께 삭제하기

khds 2021. 8. 15. 22:07

프로젝트에서 하나의 엔티티를 삭제했을 때 이 엔티티와 연관된 다른 엔티티는 어떻게 될까?? 정답은 그 엔티티의 외래 키로 연결된 엔티티에서는 부모 엔티티가 없기 때문에 데이터베이스에서 외래 키 무결성 예외가 발생한다. 예를 들어 User 엔티티와 Article 엔티티가 있다고 보자. User와 Article는 일대다의 관계를 가진다. User 하나에 여러 개의 Article가 있는 셈이다. 여기서 만약 User를 삭제한다면 삭제한 User에 연결된 Article에서는 에러가 발생하는 것이다. 다시 말해 연관관계가 끊어졌다고 할 수 있다. 이러한 상황에서  User와 연관관계가 끊어진 Article를 고아 객체라고 한다. 그렇기에 부모 엔티티를 삭제할 때는 연관된 자식 엔티티를 모두 삭제한 후에 부모 엔티티를 삭제해야 한다. 하지만 이는 코드도 증가하고 번거로운 과정일 것이다. 이럴 때 부모를 삭제하면 자동으로 자식들도 삭제된다면 얼마나 좋을까?!

그러기 위한 방식은 두가지 있다.

 

첫 번째 방식은 아래와 같다. 

 

@Entity
public class User{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @OneToMany(mappedBy = "user", orphanRemoval = true)
    private List<Article> articles = new ArrayList<>();
}

 

위와 같이 orphanRemoval = true를 설정해주면 된다.

이렇게 하면 User를 삭제할 시 연관관계가 있는 Article들도 같이 삭제될 것이다. 

삭제 순서는 외래 키 제약 조건을 고려해서 자식을 먼저 삭제하고 부모를 삭제한다.

또한 부모 엔티티에서 자식엔티티에 대한 참조 즉, articles에서 특정 article를 없애기만 해도 해당되는 article이 삭제된다는 것이다. 

여기서 주의할 점은 특정 엔티티가 개인 소유하는 엔티티에만 이 기능을 적용해야 한다. 만약 삭제한 엔티티를 다른 곳에서도 참조한다면 문제가 발생할 수 있다. 이런 이유로 orphanRemoval은 @OneToOne, @OneToMany에만 사용할 수 있다.

 

두 번째 방식은 영속성 전이 특성을 이용하는 것이다. 아래는 예시 코드이다. 

 

@Entity
public class User{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE)
    private List<Article> articles = new ArrayList<>();
}

 

cascade = CascadeType.REMOVE를 추가하면서 User를 삭제할 시 Article도 같이 삭제할 수 있다. 

참고로 cascade를 단다는 것은 영속성 전이 특성을 부여한다는 것인데 영속성 전이란 특정 엔티티를 영속성 상태로 만들 때 연관된 엔티티도 함께 영속성 상태로 변경한다는 것을 의미한다. 그렇기에 단순이 삭제 기능뿐만이 아니라 다른 기능들도 있다. 

다른 기능들은 아래와 같다.

 

public enum CascadeType {
    All,     // 모두 적용
    PERSIST, // 영속
    REMOVE,  // TKRWP
    MERGE,   // 병합
    REFRESH, // REFRESH
    DETACH,  // DETACH
}

 

그렇다면 orphanRemoval = true 와 cascade = CascadeType.REMOVE의 차이는 무엇일까??

orphanRemoval = true 는 연관된 엔티티 간의 참조가 끊어질 때 삭제가 이루어진다. 

하지만 cascade = CascadeType.REMOVE 는 부모 엔티티를 삭제하면 자식 엔티티를 삭제하는 것이지 참조가 끊어질 때 삭제가 이루어지는 것은 아니다. 

 

 

참고

자바 ORM 표준 JPA 프로그래밍 - 김영한