사용법
CascadeType.ALL는 아래와 같이 쓴다.
@OneToMany(cascade = CascadeType.ALL)
private List<Member> members = new ArrayList<>();
@OnDelete는 아래와 같이 쓴다.
@OneToMany
@OnDelete(action = OnDeleteAction.CASCADE)
private List<Member> members = new ArrayList<>();
참조무결성과 Cascade
create table Member (
foreign key (member_id) references Team (id)
);
참조 무결성에 대한 개념을 복습하자면 Team과 Member 연관관계에 있고 Team의 기본키와 member_id 외래키를 설정할 경우 Team의 기본키 데이터를 삭제할 경우 연관된 컬럼(외래키 데이터)이 존재하기 때문에 삭제가 되지 않는 것을 참조 무결성이 제약조건이라 한다.
Cascade는 이렇게 참조무결성이 걸렸을 때 연관된 데이터를 삭제 혹은 저장을 하는 종속적 행위를 말한다.
create table Member (
foreign key (member_id) references Team (id) ON DELETE CASCADE;
);
위와 같이 설정해주면 Team의 기본키 데이터를 삭제할 경우 연관된 컬럼이 모두 삭제된다.
@OnDelete와 CascadeType.ALL의 차이
CascadeType의 속성은 여러가지(ALL, REMOVE, PERSIST)가 있는데 ALL로 쓰면 모든 속성을 사용한다.
그래서 @OnDelete와 CascadeType.ALL은 둘 다 Cascade의 DELETE 기능을 갖고 있다.
즉, 기본키 데이터를 삭제할 경우 외래키 데이터도 전부 삭제된다.
다만 @OnDelete는 프로그램 올라오기 전에 DB 테이블에 접근해서 위처럼 ON DELETE CASCADE를 붙여주는 것이다.
CascadeType.ALL 기능은 테이블을 바꾸는 것이 아니라 런타임 도중에 JPA에 의해 실행되도록 만드는 것이다.
JPA는 @OnDelete에 표준화되어있지 않다.
JPA 자체가 데이터베이스 저장소에 구애받지 않고(storage-agnostic) 사용 하기 위한 기술이기 때문에 테이블을 변경하는 것을 바람직하지 못하다고 여긴다.
결론은 @OnDelete는 지양하자.
고아 객체와 orphanRemoval
@OneToMany( cascade = CascadeType.ALL, orphanRemoval = true)
private List<Member> members = new ArrayList<>();
team.getMembers().remove(0);
고아 객체는 부모 엔티티로 부터 끊어진 자식 엔티티를 말한다.
위와 같이 orphanRemoval 속성을 true로 걸고 remove(0)을 통해서 team(부모)에 의해서 자식 엔티티를 끊는다면 자동으로 delete 쿼리가 나간다.
orphanRemoval 역시 CascadeType.ALL처럼 부모 엔티티가 삭제되면 자식도 다 삭제되는 기능을 갖고있다.
orphanRemoval과 CascadeType.ALL의 차이
CascadeType.ALL만 걸었을 경우엔 부모 엔티티에 의해서 자식 엔티티를 끊을 경우 orphanRemoval과 달리 Delete 쿼리가 나가지 않는다.
그렇기 때문에 DB 역시 데이터가 그대로 남아있다.
그래서 CascadeType.ALL과 orphanRemoval을 같이 걸면 orphanRemoval의 부모 엔티티로부터 자식 엔티티를 끊을 때 delete 쿼리가 나가는 기능만 가져올 수가 있다.
보통 두 개를 같이 쓰는 경우는 DDD의 Aggregate Root 개념을 구현할 때 사용한다.
Aggregate란 연관된 엔티티의 묶음이고 Aggregate Root는 루트가 되는 엔티티를 말한다.
위의 그림처럼 Viny1이 Root고 그 밑에 연관된 Entity가 존재한다.
이럴 경우 Root에 한해서만 연관된 Entity의 생명주기를 관리하는 것을 Aggregate Root 개념이라고 한다.
위의 개념을 구현하려면 Root에만 Repository를 만들어서 관리한다. 마찬가지로 orphanRemoval 역시 부모 Entity로부터 자식의 생명주기를 관리하게 되는 것이니 Aggregate Root 개념을 적용하고 싶을 때 사용할 수 있다.
'📖 ORM' 카테고리의 다른 글
Fetch Join 사용 시 조건문(Condition) 올바르게 사용하기 - 실습으로 배우는 JPA 4편 (0) | 2022.11.03 |
---|---|
@Transactional 사용 시 자기 호출(Self-Invocation) 이슈 - 실습으로 배우는 JPA 3편 (2) | 2022.07.06 |
findAll()에 관한 N+1 테스트 - 실습으로 배우는 JPA 2편 (0) | 2022.06.30 |
@ManyToOne과 @OneToMany로 배우는 JPA 기초 사용법 - 실습으로 배우는 JPA 1편 (2) | 2022.06.20 |
Kotlin 에서의 JPA Builder (0) | 2021.03.29 |