영속성 전이(CASCADE)
특정 엔티티를 영속 상태로 만들 떄 연관된 엔티티도 함께 영속상태로 만들고 싶을 때
ex) 부모 엔티티를 저장할 떄 자식 엔티티도 함께 저장
Parent1 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]) var childList: MutableList<Child>? = mutableListOf()
fun addChild(child: Child) { childList?.add(child) child.parent = this } }
|
Child1 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 }
|
jpaMain1 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(); } }
|
result1 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 into Parent (name, MEMBER_ID) values (?, ?) Hibernate: insert into Child (name, parent_id, MEMBER_ID) values (?, ?, ?) Hibernate: 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
Parent1 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 } }
|
jpaMain1 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(); } }
|
result1 2 3 4 5 6
| Hibernate: delete from Child where MEMBER_ID=?
|
orphanRemoval
옵션을 준 자식은 Collection에서 빠지면 delete 쿼리가 실행되서 삭제가 된다.
고아 객체 주의 사항
- 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능
- 참조하는 곳이 하나일 때 사용해야된다!
- 특정 엔티티가 개인 소유할 때 사용
- @OneToOne, @OneToMany만 가능
영속성 전이 + 고아 객체 생명주기
- CascadeType.ALL + orphanRemoval=true
- 스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 제거
- 두 옵션을 모두 활성화 하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있음
- 도메인 주도 설계(DDD)의 Aggregate Root개념을 구현할 떄 유용
소스코드
참조