Cascade와 orphanRemoval차이점

이수찬·2023년 4월 14일
0

1. CascadeType.REMOVE의 사용 이유

  • member entity
@Getter
@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "member_id")
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String email;

    private String phone;

    @Column(nullable = false)
    @Enumerated(STRING)
    private MemberRole memberRole;

    @OneToMany(fetch = LAZY, mappedBy = "member")
    private List<Item> itemList = new ArrayList<>();
    
    ...
    
}
  • item entity
@Entity
@Getter
@NoArgsConstructor(access = PROTECTED)
public class Item extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id")
    private Long id;

    private String name;

    private Long price;

    @Column(columnDefinition = "text")
    private String description;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id")
    private Category category;

    // 관리자, 구매자 구분
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
    private Member member;
  • memberService
public void deleteMember(DeleteMemberRq rq) {
        memberRepository.deleteById(rq.getMemberId());
    }

위의 코드를 실행하면 다음과 같이 예외가 나온다.
db에서 memberId가 2인 member를 삭제하려 했는데, member가 item을 이미 등록하고 있어, membver를 삭제할 수 없다.

이때 필요한 것이 Cascade.REMOVE이다.

2. CascadeType.REMOVE?

parent entity가 삭제되면, 자식 엔티티(부모 엔티티와 연관된 엔티티)도 같이 삭제된다.
위의 예제에서는 member를 삭제하면, member가 등록한 item들도 모두 삭제된다.

  • member entity에 CascadeType.REMOVE 추가
@OneToMany(fetch = LAZY, mappedBy = "member", cascade = CascadeType.REMOVE)
    private List<Item> itemList = new ArrayList<>();
  • 실제 삭제 쿼리
Hibernate: 
    select
        m1_0.member_id,
        m1_0.email,
        m1_0.member_role,
        m1_0.name,
        m1_0.phone 
    from
        member m1_0 
    where
        m1_0.member_id=?
Hibernate: 
    select
        i1_0.member_id,
        i1_0.id,
        i1_0.dtype,
        i1_0.category_id,
        i1_0.created_at,
        i1_0.description,
        i1_0.name,
        i1_0.price,
        i1_0.updated_at,
        i1_0.cpu,
        i1_0.ram,
        i1_0.ssd,
        i1_0.item_id,
        i1_0.origin 
    from
        item i1_0 
    where
        i1_0.member_id=?
Hibernate: 
    delete 
    from
        item 
    where
        id=?
Hibernate: 
    delete 
    from
        item 
    where
        id=?
Hibernate: 
    delete 
    from
        item 
    where
        id=?
Hibernate: 
    delete 
    from
        member 
    where
        member_id=?

위의 쿼리를 보자.

member 삭제는 아래와 같이 진행된다.
1. 삭제할 member를 id로 조회
2. 삭제할 memberId로 등록한 item들 조회
3. 연관된 item들 모두 삭제(member가 등록한 item이 3개이므로
쿼리 3개 발생)
4. member 삭제

이러면 cascade = CascadeType.REMOVE 만 사용해도 될꺼 같은데, orphanRemoval는 왜 쓰는 것일까?

이를 알기 위해 member가 만든 item을 삭제해보자.

  • member entity orphanRemoval = true 추가
@OneToMany(fetch = LAZY, mappedBy = "member", cascade = CascadeType.REMOVE, orphanRemoval = true)
    private List<Item> itemList = new ArrayList<>();
  • member로 member가 만든 item 삭제하기
public void deleteItem(DeleteMemberRq rq) {
        Member member = memberRepository.findById(rq.getMemberId())
            .orElseThrow(CannotFindMemberException::new);
        member.getItemList().remove(0);
    }

이때, CascadeType.REMOVE의 경우 관계가 끊어진 것을 제거로 보지 않기에 자식 클래스에는 값이 null 값으로 대체된다.

  • orphanRemoval=true?
    CascadeType.REMOVE와 동일하게 parent entity가 삭제되면, 자식 엔티티도 같이 삭제되지만, 관계가 끊어진 child를 자동으로 제거한다. (child 테이블의 row를 제거한다)

1-1. CascadeType.ALL orphanRemoval=true를 함께 사용 가능한 경우

  • child를 관리하는 parent테이블이 오직 한개 존재하는 경우 사용 가능(다른 엔티티가 child를 알고있는 경우 사용 불가능)

  • child가 다른 엔티티를 알고있는 경우는 사용 가능.

  • 즉, 고아객체는 참조하는 곳이 하나일 때만 사용 가능하다
    (특정 엔티티가 특정 엔티티의 개인 소유일 때 사용 가능)

  • 즉,부모 엔티티를 통해서 자식의 생명주기가 관리가능하다는 의미입니다.

  1. db의 cascade와 jpa cascade delete 차이는?
  • JPA의 Cascade
    특정 엔티티를 영속 상태로 만들 때 연관 엔티티도 함께 영속화

  • DATABASE의 Cascade(관계형 DB의 데이터를 삭제할 때 사용하는 @OnDelete)
    DB의 무결성 제약조건 중 FOREIGN EKY 제약 조건의 옵션 중 하나로 부모 테이블에서 해당 행이 업데이트 되거나 삭제될 때 참조 테이블에서도 해당 행이 업데이트 혹은 삭제

  • 차이점
    casecade=CascadeType.REMOVE
    JPA 레벨에서 작동
    JPA가 부모 엔티티를 삭제할 때 연관된 자식 데이터에 대한 DELETE 쿼리를 실행 함
    자식 엔티티를 삭제하는 쿼리가 먼저 실행되고, 그 다음 부모 엔티티를 삭제하는 쿼리가 실행 된다.

@OnDelete(action= OnDeleteAction.CASCADE)만 설정하여 위 요구사항을 충족시켰다!
위와 같은 에러는 발생하지 않지만 자식 엔티티 하나만 삭제해도 부모와 관련있는 모든 자식 엔티티까지 삭제 된다.

  1. 삭제로직으로 어떻게 차이가 있는지 정리해보기

0개의 댓글