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


184

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

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

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

건배

답변:


292

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

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

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


3
이것은 그들이 안전한 효과를 가지고 있지만 다른 시스템이 그것을 책임지게 만드는 것을 의미합니까?
Anonymoose

101
아논, 같은 효과가 없습니다. ON DELETE CASCADE는 부모가 삭제 될 때 모든 자식 레코드를 삭제하도록 DB에 지시합니다. 즉, INVOICE를 삭제 한 다음 해당 INVOICE의 모든 항목을 삭제하십시오. OrphanRemoval은 ORM에 송장 개체 (메모리 작업 중)에 속하는 항목 컬렉션에서 Item 개체를 제거한 다음 송장을 "저장"하면 제거 된 항목을 기본 DB에서 삭제해야한다고 ORM에 알립니다.
garyKeorkunian

2
단방향 관계를 사용하는 경우 orphanRemoval = true를 설정하지 않아도 고아가 자동으로 제거됩니다.
Tim

98

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

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

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

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

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

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

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


귀하의 답변을 읽은 후, 두 문제와 내 문제의 정확한 차이점이 해결되었습니다. 부모 엔터티의 정의 된 컬렉션에서 연결이 끊어지면 데이터베이스에서 자식 엔터티를 삭제하는 데 어려움을 겪었습니다. 동일하게 ' stackoverflow.com/questions/15526440/… ' 라는 질문을했습니다 . 두 질문을 연결하기 위해 내 의견을 추가하십시오.
Narendra Verma 2013 년


46

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


3
"개인"부모 / 자식 관계라고도합니다.
HDave

전화 department.remove(emp);commit()
걸지

18

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


1
cascade=CascadeType.REMOVE와 동일하지 않습니다 ON DELETE CASCADE. 응용 프로그램 코드에서 제거하고 DDL, DB에서 실행되는 다른 DDL에는 영향을 미치지 않습니다. stackoverflow.com/a/19696859/548473
Grigory Kislin

9

차이점은 다음과 같습니다.
-orphanRemoval = true : "자식"엔터티가 더 이상 참조되지 않으면 제거됩니다 (부모가 제거되지 않을 수 있음).
-CascadeType.REMOVE : "자식"개체는 "부모"가 제거 된 경우에만 제거됩니다.


6

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

엔터티 상태 전환

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. 이는 여러 레벨에 많은 하위 엔티티가있는 루트 엔티티를 제거하는 경우 매우 위험한 작업 일 수 있습니다.

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


따라서 고아 제거에서 답의 일부 : post.getComments (). remove (postComment); Persist 캐스케이드 때문에 OneToMany 양방향 매핑에서만 작동합니다. 예를 들어 계단식이없고 ManyToOne 측에서 제거하지 않으면 두 엔티티 간의 연결 제거가 DB에서 지속되지 않습니까?
aurelije

고아 제거는의 영향을받지 않습니다 CascadeType. 보완적인 메커니즘입니다. 지금, 당신은 지속으로 제거를 착각하고 있습니다. 고아 제거는 참조되지 않은 연결을 삭제하는 반면 지속은 새 엔티티를 저장하는 것입니다. 이러한 개념을 더 잘 이해하려면 답변에 제공된 링크를 따라야합니다.
Vlad Mihalcea

M 측에서 연결을 제거하지 않으면 어떻게 양방향 매핑에서 고아 제거가 시작됩니까? PostComment.post를 null로 설정하지 않고 Post 목록에서 PostComment를 제거해도 DB의 두 엔티티 간 연결이 제거되지는 않는다고 생각합니다. 그래서 저는 고아 제거가 시작되지 않을 것이라고 생각합니다. 관계 세계에서 PostComment는 고아가 아닙니다. 자유 시간이되면 시험해 보겠습니다.
aurelije

1
고성능 Java Persistence GitHub 리포지토리에이 두 가지 예제를 추가 하여 모든 작동 방식을 보여줍니다. 일반적으로 엔터티를 직접 제거하기 위해 수행해야하므로 자식 측을 동기화 할 필요가 없습니다. 그러나 고아 제거는 계단식을 추가 한 경우에만 작동하지만 JPA 사양이 아니라 최대 절전 모드 인 것으로 보입니다.
Vlad Mihalcea

5

@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)가 데이터베이스에서 삭제됩니다.


1
두 번째 단락에 오타가 있습니다. entityManager.delete (obj)와 같은 방법은 없습니다. entityManager.remove (obj)입니다.
JL_SO

3

고아 제거는 다음과 같은 시나리오에서 ON DELETE CASCADE와 동일한 효과를 갖습니다. Student 테이블이 id_guide를 FK로 갖도록 Student와 Guide 테이블 간의 외래 키 관계.

    @Entity
    @Table(name = "student", catalog = "helloworld")
    public class Student implements java.io.Serializable {
     @Id
     @GeneratedValue(strategy = IDENTITY)
     @Column(name = "id")
     private Integer id;

    @ManyToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    @JoinColumn(name = "id_guide")
    private Guide guide;

// 부모 엔티티

    @Entity
    @Table(name = "guide", catalog = "helloworld")
    public class Guide implements java.io.Serializable {

/**
 * 
 */
private static final long serialVersionUID = 9017118664546491038L;

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;

@Column(name = "name", length = 45)
private String name;

@Column(name = "salary", length = 45)
private String salary;


 @OneToMany(mappedBy = "guide", orphanRemoval=true) 
 private Set<Student> students = new  HashSet<Student>(0);

이 시나리오에서 관계는 학생 실체가 관계의 소유자가되므로 전체 객체 그래프를 유지하기 위해 학생 실체를 저장해야합니다.

    Guide guide = new Guide("John", "$1500");
    Student s1 = new Student(guide, "Roy","ECE");
    Student s2 = new Student(guide, "Nick", "ECE");
    em.persist(s1);
    em.persist(s2);

여기서 우리는 동일한 안내서를 두 개의 다른 학생 객체와 매핑하고 CASCADE.PERSIST를 사용하기 때문에 객체 그래프가 데이터베이스 테이블에 다음과 같이 저장됩니다 (내 경우에는 MySql)

학생 테이블 :-

아이디 이름 부서 Id_Guide

1 로이 ECE 1

2 닉 ECE 1

가이드 테이블 :-

아이디 이름 급여

요한 일서 $ 1500

이제 학생 중 한 명을 제거하려면

      Student student1 = em.find(Student.class,1);
      em.remove(student1);

학생 레코드가 제거되면 해당 가이드 레코드도 제거해야합니다. 여기서 학생 엔터티의 CASCADE.REMOVE 속성이 표시되고 그 역할은 수행됩니다. 식별자가 1 인 학생과 해당 가이드 개체 (식별자)가 제거됩니다. 1). 그러나이 예제에는 동일한 안내서 레코드에 맵핑되는 하나 이상의 학생 오브젝트가 있으며, Guide Entity에서 orphanRemoval = true 속성을 사용하지 않으면 위의 제거 코드가 작동하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.