Spring Data JPA - Cascade

Spring Data JPA

백기선님의 강의인 Spring Data JPA 강의를 듣고 공부한 내용을 정리한 글

Cascade

Cascade 옵션이란?

엔티티의 상태 변화를 전파시키는 옵션이다.

OneToMany와 ManyToOne로 양방향 관계를 맺는 엔티티의 상태 변화를 전이시킬 때 사용한다.

엔티티의 상태란?

스크린샷 2020-04-28 오후 10 23 27

이 그림만 봐서는 뭐가 뭔지 하나도 모르겠다.

우선 하나 씩 차근 차근 살펴보자.

  • 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



© 2022. by minkuk

Powered by minkuk