[TIL] orphanRemoval=true 혼자서 작동되지 않는 버그

phdljr·2023년 12월 27일
0

TIL

목록 보기
49/70

팀원들과 JPA에 대해 얘기를 나누던 도중, orphanRemovalCascadeType.REMOVE에 살펴보는 시간을 갖게 되었다.

두 개의 차이가 무엇인지 궁금하여 검색도 해보고 테스트도 진행하게 되었다.

그러나, 전혀 예상치 못한 상황을 겪게 되었다.

이러한 상황에 대해 공유하고자 한다.


영속성 전이

특정 엔티티에서 연관된 엔티티도 함께 영속 상태로 만들거나 제거할 때 사용되는 기능이다.

즉, 부모 엔티티를 저장하거나 삭제할 때, 자식 엔티티도 저장하거나 삭제되도록 영속성을 전이시키는 것이다.

단, 부모 엔티티와 자식 엔티티의 연관 관계가 끊겼다고 해도, DB에는 영향이 가진 않는다.

cascade 옵션을 통해 설정이 가능하다.

@Entity
public class Board {

    public Board() {
    }

    public Board(final Long id) {
        this.id = id;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "board", cascade = CascadeType.ALL)
    private List<Comment> comments = new ArrayList<>();
}

고아 객체 삭제

부모 엔티티와 자식 엔티티의 연관 관계가 끊겼을 때, 해당 자식 엔티티는 부모 엔티티를 가지지 않는 고아 상태가 된다.

부모 엔티티가 삭제됐을 때도 자식 엔티티를 삭제하게 된다.(cascade=CascadeType.REMOVE와 같음)

이러한 고아 엔티티를 DB에서 삭제시켜주는 기능을 의미한다.

orphanRemoval=true 옵션을 통해 설정이 가능하다.

@Entity
public class Board {

    public Board() {
    }

    public Board(final Long id) {
        this.id = id;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "board", orphanRemoval = true)
    private List<Comment> comments = new ArrayList<>();
}

고아 객체 삭제 기능의 버그 발견

orphanRemoval=true 기능을 테스트하기 위해 다음과 같은 테스트를 진행하게 되었다.

@Entity
public class Board {

    public Board() {
    }

    public Board(final Long id) {
        this.id = id;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "board", orphanRemoval = true)
    private List<Comment> comments = new ArrayList<>();
}
@Test
@Rollback(value = false)
void setup(){
    boardRepository.deleteAll();
    commentRepository.deleteAll();

    Board board = boardRepository.save(new Board(1L));
    commentRepository.save(new Comment(1L, board));
    commentRepository.save(new Comment(2L, board));
    commentRepository.save(new Comment(3L, board));
}

@Test
@Rollback(value = false)
@Transactional
public void test(){
    Board board = boardRepository.findById(1L).get();
    board.getComments().remove(0);
}

그러나 결과는 DB에 comment가 삭제되지 않았다.

그러나 orphanRemoval=truecascade=CascadeType.PERSIST를 같이 적용해서 테스트를 진행해보니 DB에 comment가 하나 삭제된 모습을 볼 수 있었다.

이러한 상황에 대해 검색을 진행해본 결과, JPA의 구현체인 Hibernate에서의 버그인 것으로 나타났다.

Hibernate의 이슈 목록에도 해당 게시글과 관련된 내용이 존재한다.
https://hibernate.atlassian.net/browse/HHH-6709

결론

orphanRemoval=true 를 사용하려면, cascade=CascadeType.PERSIST와 함께 사용해야 한다.


참조

https://www.inflearn.com/questions/137740/orphanremoval%EA%B3%BC-cascade%EC%9D%98-%EA%B4%80%EA%B3%84
https://hibernate.atlassian.net/browse/HHH-6709

profile
난 Java도 좋고, 다른 것들도 좋아

0개의 댓글