[JPA] Cascade vs orphanRemoval

손효재·2022년 9월 9일
1

JPA

목록 보기
9/11
post-thumbnail

📌 Cascade - 영속성 전이

특정 엔티티를 영속 상태로 만들때 연관된 엔티티도 함께 영속상태로 만들고 싶을때
ex) 부모 엔티티를 저장할때 자식 엔티티도 함께 저장

아래 코드에서 Parent 엔티티만 save하면 parent만 저장되고 child는 저장되지 않는다.

Untitled Untitled (1)

이때 Parent에서 cascade 타입을 지정해주면 child 엔티티도 함께 저장된다.
cascade타입을 ALL로 지정했지만, 여기서는 Persist에 해당된다.

Untitled (2)

CASCADE 종류 (CascadeType.Cascade타입)

  • ALL : 모두 적용
  • PERSIST : 영속
  • REMOVE : 삭제
  • MERGE : 병합
  • REFRESH
  • DETACH

부모를 persist 할때, cascade 지정된 매핑된 엔티티도 함께 persist 해준다

엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐 영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없음

하나의 엔티티가 해당 엔티티를 관리할때(종속적일때) 의미가 있다. 라이프 사이클이 동일하고, 단일 소유자일때 (Parent만 Child를 관리할떄) 사용하면 된다. 다른 엔티티에서도 관리하고 있을때는 사용하면 안된다!!


📌 고아객체

부모 엔티티와 연관관계가 끊어진 자식 엔티티를 고아객체라고 한다.

orphanRemoval = true

참조가 제거된 엔티티는 다른곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능이다.

ex) Parent parent1 = em.find(Parent.class, id);
parent1.getChildren().remove(0);

→ 자식엔티티를 컬렉션에서 제거하면 delete 쿼리가 나간다.

개념적으로 부모를 제거하면 자식은 고아가 된다. 따라서 고아 객체 제거 기능을 활성화 하면, 부모를 제거할 때 자식도 함께 제거된다. 이것은 CascadeType.REMOVE처럼 동작한다.


📌 CascadeType.REMOVE vs orphanRemoval = true

부모 엔티티 삭제

먼저, 아래와 같이 부모 엔티티인 Parent를 삭제해보자!

Untitled (3)

CascadeType을 ALL로 지정하여 parent가 persist될때 child도 persist시켜주고, 삭제될때는 함꼐 지워지도록 한다.

다른 방법으로 cascadeType을 지정하지않고, child를 따로 저장해준 이후, orphanRemoval = true으로 지정하고 parent를 삭제한다.

이때 둘다 동일하게 delete 쿼리가 3번 나가면서, parent와 child가 모두 지워지는것을 확인할 수 있다.
parent가 삭제될때, child도 영속성 전이 옵션으로 인해 함께 삭제된다.

Untitled (4)

결론적으로 부모 엔티티를 삭제할 때는,
CascadeType.REMOVE와 orphanRemoval = true가 동일하게 동작한다.


부모 엔티티에서 자식 엔티티 삭제

이제 자식 엔티티인 Child 엔티티를 삭제해보자!

Untitled (5)

이전과 동일하게 CascadeType을 ALL로 지정해서 영속성 전이를 통해 child를 함께 저장하고, 첫번째 child를 삭제해본다. 하지만, 이때는 delete쿼리가 나가지 않는다. 영속성 전이 삭제 옵션은 부모와 자식의 관계가 끊어졌다 해서 자식을 삭제하지 않기 때문이다.

다음으로 orphanRemoval = true를 통해 부모 엔티티가 자식 엔티티의 관계를 제거하면 자식은 고아로 취급되어 그대로 사라진다. CascadeType.PERSIST를 함께 사용하면, 이때도 부모가 자식의 전체 생명 주기를 관리하게 된다.

Untitled (6)

이전과는 다르게 delete 쿼리가 1번 나간다. 고아 객체 옵션은 부모와 자식의 관계가 끊어지면 자식을 고아로 취급하고 자식을 삭제하기 때문이다.

Untitled (7)

DB상으로도 자식 엔티티가 삭제되었다.


결과

  • 부모 엔티티 삭제

CascadeType.REMOVE와 orphanRemoval = true는 부모 엔티티를 삭제하면 자식 엔티티도 삭제한다.

  • 부모 엔티티에서 자식 엔티티 제거

CascadeType.REMOVE는 자식 엔티티가 그대로 남아있는 반면, orphanRemoval = true는 자식 엔티티를 제거한다.


❗️ 주의

  • 특정 엔티티가 개인 소유할 때(참조하는 곳이 하나일때) 사용해야한다.

예를 들어 Child(자식)을 Parent(부모)도 알고 Grand(부모)도 알고 있다면, CascadeType.REMOVE 또는 orphanRemoval = true를 주의해야한다. 자식 엔티티를 삭제할 상황이 아닌데도 어느 한쪽의 부모 엔티티를 삭제했거나 부모 엔티티로부터 제거됐다고 자식이 삭제되는 불상사가 일어날 수 있기 때문이다.

  • @OneToOne, @OneToMany만 가능

스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 생명주기를 제거한다.

두 옵션을 모두 활성화 하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있다.
CascadeType.ALL + orphanRemovel=true (영속성 전이 + 고아객체)

→ 도메인 주도 설계(DDD)의 Aggregate Root개념을 구현할 때 유용하다.
부모와 자식 관계에서 부모가 Aggregate Root가 되고 부모 레포지토리만 만들어서 개발하는 경우에서 사용된다고 볼 수 있다.

1개의 댓글

comment-user-thumbnail
2022년 9월 10일

casecade 하고 고아객체 차이점 잘보고 가요~

답글 달기