Spring Data JPA - Cascade
Spring Data JPA
백기선님의 강의인 Spring Data JPA 강의를 듣고 공부한 내용을 정리한 글
Cascade
Cascade 옵션이란?
엔티티의 상태 변화를 전파시키는 옵션이다.
OneToMany와 ManyToOne로 양방향 관계를 맺는 엔티티의 상태 변화를 전이시킬 때 사용한다.
엔티티의 상태란?
이 그림만 봐서는 뭐가 뭔지 하나도 모르겠다.
우선 하나 씩 차근 차근 살펴보자.
Transient : JPA가 엔티티를 모르는 상태, 즉 최초의 객체들이 생성된 단계
Persistent : JPA가 관리중인 상태, DB에 들어간 것을 의미하지는 않는다. 즉 save() 메소드를 사용했다고 해서 바로 DB에 insert 되는 것은 아니며 이 상태를 Persistent라고 부른다.( 1차 캐시, Dirty Checking, Write Behind 등)
Detached : JPA가 더이상 관리하지 않는 상태.
Removed : JPA가 관리하긴 하지만 삭제하기로 한 상태.
예시
@Entity
class Post (
@Id @GeneratedValue
val id:Long?,
val title:String
){
@OneToMany(mappedBy = "post",cascade = [CascadeType.PERSIST])
val comments:MutableSet<Comment> = HashSet()
fun addComment(comment:Comment){
this.comments.add(comment)
comment.post = this
}
}
@Entity
class Comment (
@Id @GeneratedValue
val id:Long?,
val comment:String,
@ManyToOne
var post:Post
)
@Component
class JpaRunner: ApplicationRunner{
@PersistenceContext
lateinit var entityManager: EntityManager;
// @Transactional이 적용된 메소드에는 트랜잭션이 적용된다. JPA를 사용할 땐 Transaction 애노테이션을 달아주도록 하자.
@Transactional
override fun run(args: ApplicationArguments?) {
val post = Post(null,"Spring Data JPA 언제 보냐")
val comment = Comment(null,"빨리 보고 싶어요",post)
post.addComment(comment)
val comment2 = Comment(null,"곧 보여드릴게요",post)
post.addComment(comment2)
// Session 클래스는 하이버네이트이다. 즉, JPA를 구현하는 하이버네이트 클래스를 가져와서 하이버네이트의 API를 사용할 수 있다.
val session = entityManager.unwrap(Session::class.java)
session.save(post)
}
}
이전의 Study-Account는 서로 독립적인 관계이다.
독립적이라는 뜻의 의미는, Study 테이블이 삭제된다고 해서 해당 테이블과 관련된 Account가 모두 삭제되지는 않는다.
즉, 두 테이블은 연관관계이지만 Parent-Child 관계는 아니다.
우리는 Cascade 예제를 위해 Parent-Child 관계인 Post, Comment 엔터티를 통해 예제를 만들었다.
게시글이 삭제되면 코멘트가 지워져야한다.
삭제를 전파하기 위해서 cascade
옵션을 사용할 수 있다.
@Entity
class Post (
@Id @GeneratedValue
val id:Long?,
val title:String
){
@OneToMany(mappedBy = "post",cascade = [CascadeType.ALL])
val comments:MutableSet<Comment> = HashSet()
fun addComment(comment:Comment){
this.comments.add(comment)
comment.post = this
}
}
명시적으로 주는 것도 가능하지만 일반적으론 ALL을 줘서 모든 상태를 반영시킨다.
@Transactional
override fun run(args: ApplicationArguments?) {
val post = Post(null,"Spring Data JPA 언제 보냐")
val comment = Comment(null,"빨리 보고 싶어요",post)
post.addComment(comment)
val comment2 = Comment(null,"곧 보여드릴게요",post)
post.addComment(comment2)
// Session 클래스는 하이버네이트이다. 즉, JPA를 구현하는 하이버네이트 클래스를 가져와서 하이버네이트의 API를 사용할 수 있다.
val session = entityManager.unwrap(Session::class.java)
session.save(post)
val getPost = session.get(Post::class.java,1L)
session.delete(getPost)
}
테스트하면 Comment 테이블에서 삭제되는 Post 레코드의 PK를 FK로 갖고 있는 모든 레코드들이 삭제되는 것을 확인할 수 있다.
Reference
인프런 백기선님의 스프링 Data JPA