JPA에서 Soft Delete와 cascade 함께 적용하기
목표
- Entity는 soft delete로 delete 쿼리를 처리한다.
- 부모 데이터가 삭제 되면 자식 데이터도 모두 soft delete가 이뤄진다.
Soft Delete
먼저 softDelete를 entity에 바로 적용하기 위해
entity에@SQLDelete 어노테이션을 적용하여 delete쿼리 발생 시 자동으로 update 쿼리가 날아가도록 하였다.
@Entity
@SQLDelete(sql = "Update goods SET is_deleted = true WHERE id = ?")
@SQLRestriction("is_deleted = false")
@Table(name = "goods")
class Goods(
...
Cascade
이 후 delete쿼리 발생 시 연관 관계에 있는 다른 entity에도 cascade로 soft delete가 이뤄지도록 하려고 했다.
발생 문제
처음 cascade 적용 시 대부분의 entity가 @ManyToOne 단방향 매핑으로 되어 있었기 때문에
모든 @ManyToOne 에 @OnDelete(action = OnDeleteAction.CASCADE) 를 적용하는 방식으로 했다.
하지만 원하는 대로 영속성 전이가 일어나지 않았음.
그냥 아무일도 일어나지 않음.
원인 분석
우선 @OnDelete 자체가 DDL을 이용해 데이터베이스의 테이블에 직접 제약 조건을 거는 방식이었음.
하지만 우리 프로젝트에서는 ddl-auto가 아닌 DB에서 직접 생성으로 테이블을 생성하여서 아무일도 안일어난 것.
그래서 ddl-auto를 이용해 테이블을 생성한 뒤 변화를 봤는데, 역시나 아무일도 일어나지 않음,
DB에 cascade 제약조건이 걸려 있다고 하더라도, 우리는 Soft Delete를 이용하기 때문에 실제 record는 delete 되지 않고 그대로 있기 때문에 cascade 제약조건도 동작하지 않음.
해결 방법
부모 Entity에서
@OneToMany 에 옵션으로 cascade 조건을 주면 원래 목표했던 soft delete 상태에서도 영속성 전이가 일어남.
@OneToMany 는 @OnDelete 와는 다르게 jpa에서 영속성 전이를 관리하기 때문에 각 entity의 soft delete도 잘 먹힌다.
아쉬움
단지 cascade를 위해서 양방향 매핑을 하는 것이 맞을까? 하는 아쉬움이 있었음.
하지만 양방향 매핑을 하지 않을 경우 로직에서 이를 처리해야 하는데,
의존성에 따라 delete 로직을 계속 관리해주어야 한다.
user → goods → ticket → review
결론
그래서 결국 양방향 매핑으로 @OneToMany 에 cascade를 걸어 jpa에서 자동으로 관리하도록 해주는 방식을 선택하고,
사용하지 않은 @OneToMany property는 private으로 접근을 막았다.