자바 ORM 표준 JPA 프로그래밍 - 기본편 CascadeType.REMOVE orphanRemoval = true

60jong·2023년 3월 3일
0

JPA

목록 보기
4/5
post-thumbnail

Cascade, 영속성 전이

영속성 전이란 연관관계 매핑에 추가할 수 있는 설정으로

한 엔티티가 영속될 때, 연관된 엔티티도 같이 영속될 수 있도록 해주는 설정이다.

즉, 여러 em.persist를 하나의 em.persist로 줄여주는 설정이다.

CascadeType

자주 쓰이는 CascadeType에는

  • ALL - PERSIST + REMOVE
  • PERSIST - 모두 같이 영속화
  • REMOVE - 모두 같이 삭제

가 있다.

예시

Parent 1 : * Child 의 관계를 예시로 들겠다.


Parent Entity

@Entity
public class Parent {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @OneToMany(mappedBy = "parent")
    private List<Child> children = new ArrayList<>();

    public void addChild(Child child) {
        children.add(child);
        child.setParent(this);
    }
}

Child Entity

@Entity
public class Child {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private Parent parent;
}

위 상황은 양방향 연관관계가 매핑돼있다. 이 상황에서 일반적인 영속화는

  • Parent 객체 영속화
  • Child 객체 영속화

순서이다.

하지만 이렇게 연관된 엔티티들을 일일이 영속화시키는 것은 실수가 있기 마련이다. 따라서 연관관계를 매핑하는 설정에 cascade = CascadeType.xxx를 추가해주면 영속성을 전이할 수 있게 된다.

CascadeType.REMOVE

그렇다면 CascadeType.REMOVE는 직관적으로 한 Entity를 삭제하면, 연관된 Entity들도 삭제된다고 생각이 든다.

실제로 em.remove(parent)를 실행해도

Hibernate: 
    /* delete hellojpa.domain.Child */ delete 
        from
            Child 
        where
            id=?
Hibernate: 
    /* delete hellojpa.domain.Parent */ delete 
        from
            Parent 
        where
            id=?

두 개의 query가 내보내지고 있는 것을 확인할 수 있다.

하지만

Parent와 Child는 양방향 연관관계 매핑이 되어있다.

따라서parent.getChildrent().remove(0)을 통해 연관관계를 끊을 수 있다. 그렇다면 이는 실제로 연관된 Child Entity가 삭제되는 것일까??

정답은 아니다. 단순히 두 엔티티 사이에 연관관계만 끊어질 뿐, Child 엔티티는 삭제되지 않는다. 어쩌면 영속성 전이에 대한 개념을 확실히 하지 않았기에 이 질문에 헷갈렸을 것이다.

영속성 전이는, 한 엔티티가 영속화 / 삭제 등의 행위가 이루어질 때 연관된 엔티티에도 같은 행위가 전이되는 것이기 때문에 당연히 연관관계를 끊는다고 해서 엔티티가 삭제되지 않는다.

orphanRemoval = true

우리는 연관관계를 끊는 순간 관계가 끊어지게 된 엔티티를 삭제하고 싶다.

Parent 엔티티에서 연관관계를 끊게 되면, 연관관계를 잃게된 Child 엔티티를 삭제하려면, Parent 엔티티의 연관관계 설정에 orphanRemoval = true를 추가하면 된다. 말 그대로 고아를 삭제하는 것이다.


Cascade와 orphanRemoval를 항상 쓰는 것이 좋을까?

상황에 따라 다르다고 생각한다.

Cascade 없이

굳이 Cascade를 사용하지 않더라도 개발하는 방식에 따라 문제없이 연관관계를 설정할 수 있다.

Parent parent = new Parent();
Child child = new Child();

orphanRemoval 없이

parent쪽에서 연관관계를 끊는 것이 아니라 child쪽에서 연관관계를 끊게 된다면 이 설정은 필요가 없다.

// need orphanRemoval
parent.removeChild(child);

******************************
// Parent Entity의 Method
public void removeChild(Child child) {
	this.children.remove(child);
}


// don't need orphanRemoval
child.removeAssociations();
em.remove(child);

******************************
// Child Entity의 Method
public void removeAssociations() {
	this.parent.removeChild(this);
}

위의 두 가지 방법 중 무엇이 더 좋은 지는 개발자의 상황에 따라 다를 것이다. 이처럼 각자 개발하고자 하는 규칙을 세우고, 이에 맞는 설정을 통해 더욱 효율적인 개발을 진행하면 될 것 같다.

진짜 결론

  • Cascade 설정은 한 Entity를 영속 / 삭제 함으로써 연관된 Entity에도 똑같이 하게 해준다.
  • orphanRemoval = true는 한 Entity에서 연관관계를 끊으면, 고아 Entity는 삭제해준다.
profile
울릉도에 별장 짓고 싶다

0개의 댓글