Posts 영속성 전이와 로딩 전략
Post
Cancel

영속성 전이와 로딩 전략

영속성 전이 - cascade type

cascade type는 영속성 전이에 대한 방법을 어떻게 할 것이냐를 의미한다. 즉 연관관계로 연결된 엔티티를 영속성 컨텍스트로 관리할 때 해당 엔티티와 연결된 엔티티를 어떤 상태로 처리할 것이냐를 의미한다.

영속성 전이 설정은 연관관계의 주인이 아닌 객체에 설정하며 이에 따라 양방향 연결인 경우에 설정이 가능하다. 이 때 Cascade설정을 위한 조건은 아래와 같다.

  • 영속성이 전이되는 엔티티와 영속성을 전이하는 엔티티의 생명주기는 동일하거나 거의 비슷해야 한다.
  • 영속성이 전이되는 엔티티는 영속성을 전이하는 엔티티에서만 사용해야 한다.

에를 들어 게시글과 댓글의 관계에서 게시글에 대한 엔티티는 영속성을 전이하는 엔티티이고 댓글은 게시글로부터 영속성이 전이되는 엔티티이다. 이 때 연관관계의 주인은 다수인 댓글 엔티티이며 Cascade설정은 게시글 엔티티에서 진행된다.

이러한 조건에서 댓글은 게시글이 조회될 때 함께 조회되고 게시글이 삭제될 때 함께 삭제된다. 또한 게시글이 존재해야 해당 게시글과 연관되는 댓글이 존재할 수 있기 때문에 게시글과 댓글의 생명주기는 거의 동일하다고 볼 수 있다. 또한 댓글은 게시글과 함께 조회되는 경우가 아닌 곳에서 사용되지 않기 때문에 위의 조건을 모두 만족하게 된다.

영속성 전이에 대한 설정의 종류는 총 6가지로 ALL, PERSIST, REMOVE, MERGE, REFRESH, DETACH가 있다.

CascadeType.PERSIST

엔티티를 영속화할 때 연관관계에 있는 엔티티도 함께 영속화하는 옵션이다.

예를 들어 위의 코드에서 게시글을 영속 상태로 변경하면 해당 게시글와 연관된 댓글도 함께 영속 상태로 전환된다.

CascadeType.MERGE

엔티티의 상태가 변경되어 이전 엔티티의 상태에 병합할 때 연관관계에 있는 엔티티도 함께 병합을 진행하는 옵션이다.

예를 들어 게시글의 내용에 수정이 발생하고 해당 게시물의 댓글에도 수정이 발생하는 경우 게시글에 대한 병합만 발생하더라도 해당 게시글과 연관된 댓글의 변경사항도 자동으로 병합된다.

CascadeType.REMOVE

엔티티를 제거하면 연관관계에 있는 엔티티도 함께 삭제하는 옵션이다.

예를 들어 게시글을 삭제하면 해당 게시글과 연관된 댓글도 함께 삭제된다.

CascadeType.REFRESH

영속성 컨텍스트의 엔티티 정보를 실제 데이터베이스의 값으로 새로고침하는 경우에 연관관계에 있는 엔티티도 함께 새로고침되는 옵션이다.

예를 들어 이미 영속성 컨텍스트에 존재하는 게시글에 대해 데이터베이스에서 다시 값을 조회하여 덮어쓰기를 하면 해당 게시글과 연관된 댓글도 함께 데이터베이스에서 조회되어 덮어쓰기가 발생하게 된다.

CascadeType.DETACH

엔티티를 영속성 컨텍스트로부터 분리하여 준영속 상태로 전환할 때 연관관계에 있는 엔티티도 함께 준영속 상태로 전환되는 옵션이다.

예를 들어 영속성 컨텍스트에 존재하는 게시글을 준영속 상태로 전환하면 해당 게시글과 연관된 댓글에 대한 엔티티도 모두 준영속 상태로 전환되게 된다.

CascadeType.ALL

위의 다섯가지 영속성 전이 방법을 모두 적용한다는 옵션이다.

Cascade 사용시 주의할 점

  • 참조 무결성 제약조건 위반

    참조 무결성 제약조건이란 관계형 데이터베이스에서 테이블이 참조할 수 없는 외래키를 가져서는 안된다는 조건을 말한다.

    Cascade.REMOVE나 Cascade.ALL을 사용하게 되면 엔티티 삭제 시 연관관계에 있는 엔티티가 함께 삭제되는데 이때 영속성 전이의 대상을 잘못 설정하는 경우 참조 무결성 제약조건을 위반하게 될 수 있다.

    예를 들어 영속성을 전이하는 객체를 댓글 엔티티로 설정하고, 영속성이 전이되는 객체를 게시글 엔티티로 설정한 경우에 댓글을 삭제하면 게시글이 함께 삭제되어 해당 게시글의 다른 댓글 엔티티가 존재하지 않는 게시글을 참조하게 되는 문제가 발생한다.

    이러한 문제를 방지하기 위해 객체의 생명주기를 적절하게 고려하여 영속성을 전이할 객체를 결정해야 한다.

  • 양방향 연관관계 매핑 시 충돌

    양방향으로 연결되어 있는 엔티티가 존재할 때, 엔티티의 영속화 관리 지점이 여러 개인 경우 데이터의 값을 예측할 수 없는 문제를 의미한다.

    예를 들어 일대다로 연결된 게시글과 댓글이 존재할 때, 게시글에서 Cascade.PERSIST를 사용하는 경우에 commentRepository를 통해 댓글을 삭제하고 boardRepository로 변경사항을 저장하는 과정에서 댓글을 삭제했지만 board에 save가 발생하는 과정에서 게시글과 댓글의 영속화가 함께 발생하여 댓글이 삭제되지 않는다.

    이를 해결하기 위해서는 board에도 연관관계 편의 메서드를 작성하여 댓글이 삭제되는 시점에 게시글이 참조하는 댓글도 함께 수정해야한다.

    뿐만 아니라 다른 영속성 전이 옵션에서도 양방향 연결 시 비슷한 문제가 발생할 수 있으므로 주의해야 한다.

orphanRemoval과의 차이

orphanRemoval은 부모-자식 연관관계가 있는 경우에 부모 엔티티와 연관관계가 사라진 엔티티인 고아객체에 대한 자동 삭제 여부를 결정하는 옵션이다.

CascadeType.REMOVE와 비슷하게 연관관계에 있는 부모 엔티티가 삭제되는 경우 자식 엔티티도 함께 삭제된다.

하지만 삭제가 아닌 단순 연관관계 제거의 경우 orphanRemoval은 고아 객체로 취급되어 엔티티가 데이터베이스에서 삭제되는 반면, CascadeType.REMOVE는 자식 엔티티가 삭제되지 않고 외래키의 값만 다른 값이나 null로 변경된다.

패치 전략 - fetch type

패치 전략은 JPA를 이용하여 엔티티를 조회하는 과정에서 연관관계에 있는 엔티티를 언제 로딩할 지에 대한 옵션이다.

패치 전략을 적절히 사용하지 못하는 경우 불필요한 데이터를 함께 불러오게 되거나 쿼리문의 발생 횟수가 증가하는 등의 문제가 발생할 수 있다.

패치 전략에 대한 설정의 종류에는 FetchType.EAGER, FetchType.LAZY가 있다.

FetchType.EAGER(즉시 로딩)

엔티티를 조회할 때에 연관관계에 있는 엔티티까지 한 번에 조회하는 패치 전략이다.

엔티티와 연관관계에 있는 엔티티가 반드시 같이 사용되는 경우에 주로 사용되며, @ManyToOne@OneToOne의 기본값으로 설정되어있다.

연관관계에 있는 엔티티가 nullable인 경우 left join, not null인 경우 join쿼리가 발생한다.

불필요한 데이터를 함께 불러올 수 있기 때문에 불러오는 엔티티의 데이터와 연관관계에 있는 객체의 데이터가 동시에 필요한 경우에 사용한다.

FetchType.LAZY(지연 로딩)

연관관계에 있는 엔티티를 실제로 사용할 때에 로딩하는 패치 전략으로, @OneToMany@ManyToMany의 기본값으로 설정되어있다.

엔티티가 조회되면 연관관계에 있는 엔티티에는 프록시 객체를 넣어두고 실제 해당 엔티티가 사용될 때에 조회를 진행하여 값을 가져오게 된다.

지연로딩을 사용하는 상태에서 엔티티의 인스턴스를 통해 연관관계에 있는 객체의 값에 접근하는 경우 해당 객체를 조회하기 위한 추가적인 쿼리문이 발생한다.

연관관계에 있는 엔티티의 수에 따라 쿼리문의 갯수가 증가하기 때문에 연관관계에 있는 엔티티의 데이터를 사용하지 않는 경우에 사용한다.


참고자료 :

https://tecoble.techcourse.co.kr/post

https://hongchangsub.com

https://velog.io/@yuseogi0218

https://aeliketodo.tistory.com

This post is licensed under CC BY 4.0 by the author.

엔티티의 연관 관계

상속 관계의 연결