✅ 영속성 전이(Cascade)란
@Entity
class Parent(
@Id @GeneratedValue
var id: Long? = null,
@OneToMany(mappedBy = "parent" cascade = [CascadeType.PERSIST, CascadeType.REMOVE])
val children: MutableList<Child> = mutableListOf()
) {
fun addChild(child: Child) {
children.add(child)
child.parent = this
}
}
@Entity
class Child(
@Id @GeneratedValue
val id: Long? = null,
@ManyToOne
var parent: Parent? = null
)
- 부모 엔티티의 상태 변화를 자식 엔티티에게 전파하는 기능
- 즉, 부모만
persist, remove 등을 호출해도 연관된 자식도 자동으로 같은 작업이 수행됨
cascade 속성은 연관관계 주인과는 무관하게 동작하며 속성이 없으면 child가 저장되지 않지만 속성이 있는 경우 parent만 저장해도 연관된 child까지 함께 저장
- 만약
양방향으로 설정했을 때는 예기치 않은 대량 저장/삭제가 발생할 수 있기 때문에 주의 필요
✅ CascadeType 종류와 의미
| CascadeType |
설명 |
PERSIST |
부모 저장 시 자식도 자동으로 persist() 됨 |
REMOVE |
부모 삭제 시 자식도 자동으로 삭제됨 |
MERGE |
부모 병합 시 자식도 병합됨 |
REFRESH |
부모를 DB 상태로 새로고침할 때 자식도 새로고침됨 |
DETACH |
부모 detach 시 자식도 detach |
ALL |
위 모든 cascade 동작 포함 |
- 실무에서는 주로
PERSIST, REMOVE, ALL 을 자주 사용
✅ 고아 객체 제거(Orphan Removal)
@OneToMany(mappedBy = "parent", orphanRemoval = true)
val children: MutableList<Child> = mutableListOf()
// 이렇게 사용했을 때 해당 child 객체에 대해 DELETE 쿼리 발생
parent.children.remove(child)
- 고아 객체 제거는 연관관계 주인이 끊어질 때만 작동
- 자식이 다른 부모로 이동하는 경우에도 주의 필요 → 기존 부모와의 관계가 끊기면 삭제 쿼리가 날아갈 수 있음
- 연관관계가
OneToMany인 경우 주로 사용됨
CascadeType.REMOVE는 부모를 삭제하면 자식도 삭제되지만, orphanRemoval = true의 경우 관계만 끊으면 자식이 삭제되는 동작 흐름으로 목적에 맞게 사용이 필요함
✅ 실무에서 자주 사용되는 패턴(집합적 생명주기 - aggregate root)
@OneToMany(mappedBy = "parent", cascade = [CascadeType.ALL], orphanRemoval = true)
- 부모 저장/삭제 시 자식도 함께 처리됨
- 자식만 따로
save/remove 호출하지 않음
- 따라서 유지보수성이 좋고 데이터 무결성이 확보됨