orphanRemoval과 cascade의 관계
1928
작성한 질문수 11
안녕하세요 영한님
강의 정말 잘 듣고 있습니다!
다름이 아니라 orphanRemoval과 cascade의 명확한 차이를 공부하던 도중 이해가 가지 않는 것이 있어서 질문 드립니다!
환경은 Spring Boot 2.4.2 입니다.
cascade는 엔티티의 상태변화를 전파하는 것이며, orphanRemoval는 연관관계가 끊어진 Entity는 자동으로 삭제하는 것으로 알고 있습니다.
근데 부모에서 orphanRemoval를 true로 설정 후, 컬렉션에서 자식 Entity를 삭제해도 Delete 쿼리가 아닌 update 쿼리가 발생했습니다.
그래서 여러가지 테스트 해보니 CascadeType.ALL 아니면 CascadeType.PERSIST시 orphanRemoval 이 정상적으로 동작하였습니다.
아래 코드에서 Board Entity에서 cascade 옵션을 지우면 update 쿼리가 발생하여 테스트는 실패하며, cascade 옵션을 ALL 또는 PERSIST을 사용하면 delete 쿼리가 발생하여 테스트가 정상적으로 통과합니다.
책의 p.311을 보면 orphanRemoval만 사용한 예제가 있는데 하이버네이트 버전이 올라가면서 변경된 것일까요?
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Board {
@Id
@GeneratedValue
private Long id;
private String title;
private String content;
@OneToMany(mappedBy = "board", cascade = CascadeType.PERSIST, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
@Builder
public Board(String title, String content) {
this.title = title;
this.content = content;
}
public Board addComment(Comment comment){
this.comments.add(comment);
comment.setBoard(this);
return this;
}
public Board removeComment(Comment comment){
this.comments.remove(comment);
comment.setBoard(null);
return this;
}
}
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Comment {
@Id
@GeneratedValue
private Long id;
private String comment;
@ManyToOne
@Setter
private Board board;
@Builder
public Comment(String comment) {
this.comment = comment;
}
}
@DataJpaTest(properties = "classpath:application-test.yml")
class BoardTest {
@Autowired
BoardRepository boardRepository;
@Autowired
CommentRepository commentRepository;
@DisplayName("orphanRemoval 테스트")
@Test
@Rollback(false)
public void orphanRemovalTest(){
Board board = Board.builder().title("1번글").content("1번글 컨텐츠").build();
boardRepository.save(board);
Comment comment = Comment.builder().comment("1번글 댓글").build();
board.addComment(comment);
commentRepository.save(comment);
entityManager.flush();
entityManager.clear();
Board board1 = boardRepository.findById(board.getId()).get();
board1.removeComment(board1.getComments().get(0));
List<Comment> commentAll = commentRepository.findAll();
assertThat(commentAll.size(), is(0));
}
}
답변 3
12
안녕하세요. hun님
해당 부분은 JPA 스펙상 원칙적으로 CascadeType.PERSIST이 없어도 orphanRemoval만으로 삭제되어야 하는 것이 맞습니다.
하이버네이트 구현체에서는 해당 기능에 버그가 있고, 그래서 CascadeType.PERSIST(또는 ALL)이 함께 적용되어야 동작합니다.
(찾아보니 이클립스 링크 같은 다른 구현체에서는 정상 동작한다는 이야기도 있네요.)
버그 리포트: https://hibernate.atlassian.net/browse/HHH-6709
그런데 이 부분이 실제 개발을 할 때는 크게 영향이 없는데, 그 이유는 orphanRemoval만 따로 적용하는 경우는 거의 없고, 보통 주인 엔티티가 하위 엔티티를 관리하는 경우에는 CascadeType.PERSIST + orphanRemoval을 함께 적용하기 때문입니다.
추가로 실무에서 데이터를 향후 복구나 이력 확인을 위해서, 직접적으로 삭제하는 경우도 드물기 때문에 orphanRemoval 옵션 자체를 사용하는 경우도 드뭅니다.
감사합니다.
0
"추가로 실무에서 데이터를 향후 복구나 이력 확인을 위해서, 직접적으로 삭제하는 경우도 드물기 때문에 orphanRemoval 옵션 자체를 사용하는 경우도 드뭅니다." 는 중요한 실무 데이터만 해당하는 걸까요?
예를 들어 게시글 삭제, 댓글 삭제 같은 경우는 회원의 댓글,게시글 리스트에 orphanRemoval = true를 적용해서 remove() 메소드를 통해 삭제해도 될까요?
실무 조언 관련 질문입니다.
0
39
1
H2데이터베이스 파일 생성
0
48
2
서브쿼리 강의에서 ALL 예시 관련 질문드립니다.
0
49
2
수정또는 삭제시 영속성 엔티티에 값이 무조건 있어야 하나요?
0
46
1
JPQL 메소드와 락
0
48
1
Delivery @OneToOne
0
55
1
17강 4~5분대 테이블 값 조회가 안됩니다.
0
85
2
UnsupportedOperationException 발생
0
80
3
H2 Database 연결이 안됩니다.
0
87
2
연관관계 매핑 질문드립니다.
0
78
2
h2데이터베이스 실행오류
0
103
2
persistence.xml
0
101
2
양방향 연관관계에서 연관관계의 주인(mappedBy)을 왜 꼭 정해야 하나요?
0
76
1
영속성 컨텍스트
0
61
1
JPA 프록시
0
87
1
Native Query와 MyBatis
0
62
1
영속성 컨텍스트는 어떤 메모리에 저장되는건가요?
0
81
1
임베디드 타입 예시 코드 관련 질문
0
110
3
명시적 조인에서 별칭을 주면 왜 객체에 접근할 수 있나요
0
89
3
인텔리제이 패키지 커서 단축키 질문
0
103
2
혹시 현재는 ID 데이터 타입이 String이면 안되나요?
0
134
1
양방향 연관관계 시 연관관계 주인을 설정하는 이유
0
67
1
임베디드 타입과 MappedSuperClass의 차이점이 궁금합니다.
0
95
1
데이터베이스가 초기화되는 것 같아요
1
172
2





