[hibernate] JPA orphanRemoval = true는 ON DELETE CASCADE DML 절과 어떻게 다른가요?

JPA 2.0 orphanRemoval속성 에 대해 약간 혼란 스럽습니다 .

JPA 공급자의 DB 생성 도구를 사용 ON DELETE CASCADE하여 특정 관계에 대한 기본 데이터베이스 DDL을 만들 때 필요하다는 것을 알 수 있다고 생각 합니다.

그러나 DB가 존재하고 이미 ON DELETE CASCADE관계 가있는 경우 삭제를 적절하게 단계 화하기에 충분하지 않습니까? 무엇을 않는 orphanRemoval추가합니까?

건배



답변

orphanRemoval와는 아무런 관련이 없습니다 ON DELETE CASCADE.

orphanRemoval전적으로 ORM에 특정한 것 입니다. 더 이상 “부모”엔터티에서 참조되지 않을 때 (예 : 부모 엔터티의 해당 컬렉션에서 하위 엔터티를 제거 할 때) “하위”엔터티가 제거되도록 표시합니다.

ON DELETE CASCADEA는 데이터베이스 고유의 건 은 “부모”행이 삭제 될 때, 그것은 데이터베이스에 “아이”행을 삭제합니다.


답변

예를 들어 여기에 양식이 있습니다 .

Employee엔티티 객체가 제거되고, 제거 작업은 참조에 종속되는 Address엔티티 객체. 이와 관련하여, orphanRemoval=truecascade=CascadeType.REMOVE동일하며, 만약 orphanRemoval=true지정된, CascadeType.REMOVE중복.

두 설정의 차이점은 관계를 끊는 것에 대한 응답입니다. 예를 들어, 주소 필드 null를 다른 Address개체 로 설정 하거나 다른 개체 로 설정하는 경우

  • 경우 orphanRemoval=trueIS 지정한 분리 Address인스턴스가 자동으로 제거됩니다. 이는 Address소유자 객체 (예 :)의 참조없이 존재하지 않아야 하는 종속 객체 (예 :)를 정리하는 데 유용합니다 Employee.

  • cascade=CascadeType.REMOVE지정된 경우에만 관계를 끊는 것이 제거 작업이 아니므로 자동 작업이 수행되지 않습니다.

고아 제거로 인한 댕글 링 참조를 피하려면이 기능은 개인 비공유 종속 개체를 보유하는 필드에 대해서만 활성화해야합니다.

이것이 더 명확 해지기를 바랍니다.


답변

컬렉션에서 하위 엔터티를 제거하는 순간 해당 하위 엔터티도 DB에서 제거됩니다. orphanRemoval은 또한 부모를 변경할 수 없음을 의미합니다. 직원이있는 부서가있는 경우 해당 직원을 제거하여 다른 부서에 배치 한 경우, 해당 직원을 플러시 / 커밋 할 때 데이터베이스에서 실수로 제거했습니다 (둘 중 먼저 발생). 그 부모의 자녀가 다른 부모에게 존재하지 않는 것이 확실한 한 사기는 orphanRemoval을 true로 설정하는 것입니다. orphanRemoval을 켜면 캐스케이드 목록에 자동으로 제거가 추가됩니다.


답변

DDL에 대한 동등한 JPA 맵핑 ON DELETE CASCADEcascade=CascadeType.REMOVE입니다. 고아 제거는 “부모”개체와의 관계가 파괴 될 때 종속 개체가 제거됨을 의미합니다. 예를 들어 @OneToMany항목 관리자 에서 자식 을 명시 적으로 제거하지 않고 관계 에서 자식을 제거하는 경우를 예로들 수 있습니다.


답변

차이점은 다음과 같습니다.

-orphanRemoval = true : “자식”엔터티가 더 이상 참조되지 않으면 제거됩니다 (부모가 제거되지 않을 수 있음).

-CascadeType.REMOVE : “자식”개체는 “부모”가 제거 된 경우에만 제거됩니다.


답변

이것은 매우 일반적인 질문 이므로이 답변을 기반으로하는이 기사를 작성 했습니다.

엔터티 상태 전환

JPA는 엔티티 상태 전이를 INSERT, UPDATE 또는 DELETE와 같은 SQL 문으로 변환합니다.

JPA 엔티티 상태 전환

당신은 때 persist엔티티, 당신은이 때 실행되는 INSERT 문을 예약하는 EntityManager플러시 자동 또는 수동으로.

당신은 때 remove엔티티, 당신은 지속성 컨텍스트 플러시 될 때 실행됩니다 DELETE 문을 예약하고 있습니다.

계단식 엔티티 상태 전환

편의상 JPA를 사용하면 엔터티 상태 전환을 상위 엔터티에서 하위 엔터티로 전파 할 수 있습니다.

따라서 하위 엔터티 와 연결된 상위 Post엔터티 가있는 경우 :@OneToManyPostComment

Post 및 PostComment 엔티티

엔티티 의 comments콜렉션은 Post다음과 같이 맵핑됩니다.

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

CascadeType.ALL

cascade속성은 JPA 제공자에게 엔티티 상태 전이를 상위 Post엔티티 PostComment에서 comments콜렉션에 포함 된 모든 엔티티 로 전달하도록 지시합니다 .

따라서 Post엔터티 를 제거하면

Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());

entityManager.remove(post);

JPA 제공자는 먼저 PostComment엔티티 를 제거하려고하며 모든 하위 엔티티가 삭제되면 엔티티도 삭제됩니다 Post.

DELETE FROM post_comment WHERE id = 1
DELETE FROM post_comment WHERE id = 2

DELETE FROM post WHERE id = 1

고아 제거

orphanRemoval속성을로 설정하면 trueJPA 제공자가 remove하위 엔티티가 콜렉션에서 제거 될 때 조작 을 스케줄합니다 .

우리의 경우에는

Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());

PostComment postComment = post.getComments().get(0);
assertEquals(1L, postComment.getId());

post.getComments().remove(postComment);

엔티티가 콜렉션 에서 더 이상 참조되지 않으므로 JPA 제공자는 연관된 post_comment레코드 를 제거하려고합니다 .PostCommentcomments

DELETE FROM post_comment WHERE id = 1

캐스케이드 삭제

ON DELETE CASCADEFK 수준에서 정의된다 :

ALTER TABLE post_comment
ADD CONSTRAINT fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
ON DELETE CASCADE;

일단 post행 을 삭제하면 다음을 수행하십시오 .

DELETE FROM post WHERE id = 1

연관된 모든 post_comment엔티티는 데이터베이스 엔진에 의해 자동으로 제거됩니다. 그러나 실수로 루트 엔티티를 삭제하면 이는 매우 위험한 작업 일 수 있습니다.

결론

JPA cascadeorphanRemoval옵션 의 장점은 업데이트 손실 을 방지하기 위해 낙관적 잠금 기능 을 활용할 수 있다는 것 입니다.

JPA 계단식 메커니즘을 사용하는 경우 DDL 레벨을 사용할 필요가 없습니다 ON DELETE CASCADE. 이는 여러 레벨에 많은 하위 엔티티가있는 루트 엔티티를 제거하는 경우 매우 위험한 작업 일 수 있습니다.

이 주제에 대한 자세한 내용은 이 기사를 확인 하십시오 .


답변

@GaryK의 대답은, 내가 설명을 찾는 시간을 절대적으로 큰 썼다되는 orphanRemoval = trueCascadeType.REMOVE그리고 그것은 나를 이해할 수있었습니다.

요약 : 객체 ( )를 삭제 하고 자식 객체도 제거하려는 경우 에만orphanRemoval = true 동일하게 작동합니다 .CascadeType.REMOVE entityManager.delete(object)

완전히 다른 상황에서 우리가 같은 데이터를 가져 와서 List<Child> childs = object.getChilds()자식 ( entityManager.remove(childs.get(0)) 을 제거 orphanRemoval=true하면 해당 엔티티 childs.get(0)가 데이터베이스에서 삭제됩니다.