간단 요약
CascadeType.REMOVE와 orphanRemoval은 부모 객체를 지우면 자식 객체도 지운다.
차이점은 부모 객체와 자식의 연관관계를 지우면
CacadeType.REMOVE는 자식을 삭제하지 않고
orphanRemoval은 자식을 삭제한다.
고아 객체
부모 엔티티와 연관 관계가 끊어진 객체를 의미한다.
- 부모 객체를 지우면 모든 자식 객체들은 고아 객체가 된다.
- 부모 객체가 자식 객체와의 연관관계를 삭제하면, 해당 자식 객체는 고아 객체가 된다.
CascadeType.REMOVE
- 부모 객체를 지우면 자식 객체도 지운다.
- 부모 객체가 자식 객체를 지웠을 때, 자식 객체는 삭제되지 않고 DB에 남아 있는다.
부모 객체 클래스를 만들고
@Entity
@Getter
@Setter
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
@OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
private List<Child> childs;
}
자식 객체 클래스도 만들었다.
@Entity
@Getter
@Setter
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
@Column
String name;
@ManyToOne
@JoinColumn(name = "parent_id")
Parent parent;
}
테스트 코드를 짜주고 기본적인 부모 자식 세팅을 해주었다.
@Test
@Transactional
@Rollback(value = false)
void init() {
List<Parent> parentList = new ArrayList<>();
Parent parent1 = new Parent();
Parent parent2 = new Parent();
parent1.setName("부모 1");
parent2.setName("부모 2");
parentList.add(parent1);
parentList.add(parent2);
parentRepository.saveAll(parentList);
List<Child> childList = new ArrayList<>();
Child child1 = new Child();
Child child2 = new Child();
Child child3 = new Child();
Child child4 = new Child();
child1.setName("부모 1의 자식 1");
child2.setName("부모 1의 자식 2");
child3.setName("부모 2의 자식 1");
child4.setName("부모 2의 자식 2");
child1.setParent(parent1);
child2.setParent(parent1);
child3.setParent(parent2);
child4.setParent(parent2);
childList.add(child1);
childList.add(child2);
childList.add(child3);
childList.add(child4);
childRepository.saveAll(childList);
}
밑에와 같은 테스트 코드를 짰다.
@Test
@Transactional
@Rollback(value = false)
void cascadeRemoveTest() {
// 부모 객체 삭제 테스트
Parent parent1 = parentRepository.findByName("부모 1");
parentRepository.delete(parent1);
// 부모 객체와 자식 연관 관계 제거 테스트
Parent parent2 = parentRepository.findByName("부모 2");
List<Child> childList = new ArrayList<>(parent2.getChilds());
for (Child child : childList) {
parent2.getChilds().remove(child);
}
System.out.println("부모 2의 자식 수: "+parent2.getChilds().size());
}
parent1에서는 부모 1을 삭제 할 것이고
parent2에서는 자식 2와 연관 관계를 제거 할 것이다.
Hibernate:
/* <criteria> */ select
p1_0.id,
p1_0.name
from
parent p1_0
where
p1_0.name=?
Hibernate:
select
c1_0.parent_id,
c1_0.id,
c1_0.name
from
child c1_0
where
c1_0.parent_id=?
Hibernate:
/* delete for com.study.orphan.entity.Child */delete
from
child
where
id=?
Hibernate:
/* delete for com.study.orphan.entity.Child */delete
from
child
where
id=?
Hibernate:
/* delete for com.study.orphan.entity.Parent */delete
from
parent
where
id=?
Hibernate:
/* <criteria> */ select
p1_0.id,
p1_0.name
from
parent p1_0
where
p1_0.name=?
Hibernate:
select
c1_0.parent_id,
c1_0.id,
c1_0.name
from
child c1_0
where
c1_0.parent_id=?
hibernate에서 Delete 문을 한번만 날렸다. 부모 1만 자식 객체를 지웠다.
부모 2의 자식 리스트를 sout으로 테스트를 했을 때
부모 2는 자식 리스트에서 자식 1, 2를 지운 것을 확인 할 수 있다.
호적을 팠는데... 자식은 모르는 상황...
사실 객체 지향적인 관점에서 보면 당연한 것이다.
orphanRemoval = true
부모 엔티티 수정
@Entity
@Getter
@Setter
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> childs;
}
부모 객체에서 수행되는 작업이 자식객체에도 반영이 될 수 있게 Cascade를 넣어줬다.
기존 테이블 Drop하고 다시 하면 된다.
Hibernate:
/* <criteria> */ select
p1_0.id,
p1_0.name
from
parent p1_0
where
p1_0.name=?
Hibernate:
select
c1_0.parent_id,
c1_0.id,
c1_0.name
from
child c1_0
where
c1_0.parent_id=?
Hibernate:
/* delete for com.study.orphan.entity.Child */delete
from
child
where
id=?
Hibernate:
/* delete for com.study.orphan.entity.Child */delete
from
child
where
id=?
Hibernate:
/* delete for com.study.orphan.entity.Parent */delete
from
parent
where
id=?
Hibernate:
/* <criteria> */ select
p1_0.id,
p1_0.name
from
parent p1_0
where
p1_0.name=?
Hibernate:
select
c1_0.parent_id,
c1_0.id,
c1_0.name
from
child c1_0
where
c1_0.parent_id=?
부모 2의 자식 수: 0
Hibernate:
/* delete for com.study.orphan.entity.Child */delete
from
child
where
id=?
Hibernate:
/* delete for com.study.orphan.entity.Child */delete
from
child
where
id=?
테스트에 트랜잭션 환경이 설정되어 있어
sout 이후 끝날 때 부모 객체와 연이 끊어진 자식 객체를 삭제 한 모습을 볼 수 있다.
'TIL' 카테고리의 다른 글
TIL 2023-11-22 gradle 외부 라이브러리 jar 적용 오류 (0) | 2023.11.22 |
---|---|
TIL 2023-11-21 PUT과 PATCH의 차이 (0) | 2023.11.21 |
TIL 2023-11-17 Entity 연관 관계 정리! (0) | 2023.11.17 |
TIL 2023-11-16 RestTemplate 짧은 설명 (1) | 2023.11.16 |
TIL 2023-11-15 JWT 검증 과정에서 일어나는 문제 해결 과정 (1) | 2023.11.15 |