JPA - 영속성 전이(CASCADE)와 고아 객체


  • 영속성 전이(CASCADE)
  • 고아 객체

영속성 전이(CASCADE)

특정 엔티티를 영속 상태로 만들 떄 연관된 엔티티도 함께 영속상태로 만들고 싶을 때
ex) 부모 엔티티를 저장할 떄 자식 엔티티도 함께 저장

Parent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
class Parent(
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
var id: Long? = null,

var name: String? = null,
) {
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = [CascadeType.ALL]) //cascde 추가 부분
var childList: MutableList<Child>? = mutableListOf()

fun addChild(child: Child) {
childList?.add(child)
child.parent = this
}
}
Child
1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
class Child(
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
var id: Long? = null,

var name: String? = null,
) {
@ManyToOne
@JoinColumn(name = "parent_id")
var parent: Parent? = null
}
jpaMain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fun main() {
val emf = Persistence.createEntityManagerFactory("hello")
val em = emf.createEntityManager()
val tx = em.transaction

tx.begin()

try {
var child1 = Child()
var child2 = Child()

var parent = Parent()
parent.addChild(child1)
parent.addChild(child2)

em.persist(parent)

tx.commit()
} catch (e: Exception) {
tx.rollback()
} finally {
em.close();
emf.close();
}
}
result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Hibernate: 
/* insert entity.Parent
*/ insert
into
Parent
(name, MEMBER_ID)
values
(?, ?)
Hibernate:
/* insert entity.Child
*/ insert
into
Child
(name, parent_id, MEMBER_ID)
values
(?, ?, ?)
Hibernate:
/* insert entity.Child
*/ insert
into
Child
(name, parent_id, MEMBER_ID)
values
(?, ?, ?)

결과와 같이 em.persist(parent) 에서 parent만 persist 하였는데 child에 값까지 persit 된것을 볼 수 있다.

영속성 전이(CASECADE) 사용시 주의

  • 영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없다.
  • 엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐이다
  • 부모와 자식과 연관관계가 하나일 떄만 사용하는것이 좋다. (자식이 다른곳에 연관 관계 일때는 사용하지 않는것이 좋음)

CASCADE의 종류

종류 설명
ALL 모두 적용
PERSIST 영속
REMOVE 삭제
MERGE 병합
REFRESH REFRESH
DETACH DETACH

ALL, PERSIST만 사용 하길 권장

고아(Orphan) 객체

  • 고아 객체 제거: 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제
  • orphanRemoval = true
Parent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
class Parent(
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
var id: Long? = null,

var name: String? = null,
) {
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = [CascadeType.ALL], orphanRemoval = true) //고아객체 추가 부분
var childList: MutableList<Child>? = mutableListOf()

fun addChild(child: Child) {
childList?.add(child)
child.parent = this
}
}
jpaMain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
fun main() {
val emf = Persistence.createEntityManagerFactory("hello")
val em = emf.createEntityManager()
val tx = em.transaction

tx.begin()

try {
var child1 = Child()
var child2 = Child()

var parent = Parent()
parent.addChild(child1)
parent.addChild(child2)

em.persist(parent)

em.flush()
em.clear()

val findParent = em.find(Parent::class.java, parent.id)
findParent.childList?.removeFirst()

tx.commit()
} catch (e: Exception) {
tx.rollback()
} finally {
em.close();
emf.close();
}
}
result
1
2
3
4
5
6
Hibernate: 
/* delete entity.Child */ delete
from
Child
where
MEMBER_ID=?

orphanRemoval 옵션을 준 자식은 Collection에서 빠지면 delete 쿼리가 실행되서 삭제가 된다.

고아 객체 주의 사항

  • 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능
  • 참조하는 곳이 하나일 때 사용해야된다!
  • 특정 엔티티가 개인 소유할 때 사용
  • @OneToOne, @OneToMany만 가능

영속성 전이 + 고아 객체 생명주기

  • CascadeType.ALL + orphanRemoval=true
  • 스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 제거
  • 두 옵션을 모두 활성화 하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있음
  • 도메인 주도 설계(DDD)의 Aggregate Root개념을 구현할 떄 유용

소스코드

참조