JPA OneToMany가 하위를 삭제하지 않음


158

@OneToMany부모와 자식 엔터티 간의 간단한 매핑에 문제가 있습니다. 자식 레코드는 컬렉션에서 제거해도 삭제되지 않습니다.

부모 :

@Entity
public class Parent {
    @Id
    @Column(name = "ID")
    private Long id;

    @OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent")
    private Set<Child> childs = new HashSet<Child>();

 ...
}

아이:

@Entity
public class Child {
    @Id
    @Column(name = "ID")
    private Long id;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="PARENTID", nullable = false)
    private Parent parent;

  ...
}

이제 childs Set에서 자식을 삭제하면 데이터베이스에서 삭제되지 않습니다. child.parent참조를 무효화하려고 시도했지만 작동하지 않았습니다.

엔티티는 웹 애플리케이션에서 사용되며 삭제는 Ajax 요청의 일부로 발생합니다. 저장 버튼을 눌렀을 때 삭제 된 자식 목록이 없으므로 암시 적으로 삭제할 수 없습니다.

답변:


253

JPA의 동작이 정확합니다 ( 사양에 따라 의미 ). 객체가 OneToMany 컬렉션에서 제거 되었기 때문에 단순히 삭제되지 않습니다. 이를 수행하는 공급 업체별 확장이 있지만 기본 JPA는이를 지원하지 않습니다.

부분적으로 이것은 JPA가 콜렉션에서 제거 된 것을 삭제해야하는지 실제로 알지 못하기 때문입니다. 객체 모델링 용어에서 이는 컴포지션 과 "집계 * 의 차이 입니다.

에서 구성 , 자식 개체는 부모 없이는 존재가 없습니다. 전형적인 예는 집과 방 사이입니다. 집을 삭제하면 객실도갑니다.

집계 는 더 느슨한 종류의 연결이며 코스 및 학생에 의해 대표됩니다. 코스를 삭제해도 학생은 여전히 ​​존재합니다 (아마도 다른 코스에 있음).

따라서 공급 업체별 확장을 사용하여이 동작을 강제 실행하거나 (가능한 경우) 명시 적으로 자식을 삭제하고 부모의 컬렉션에서 제거해야합니다.

나는 알고있다 :


좋은 설명 감사합니다. 그래서 내가 두려워하는 것입니다. (나는 구하기 전에 검색하고 읽었으며 그냥 저장하고 싶었습니다). 어떻게 든 JPA API를 사용하고 Hibernate를 직접 니트하기로 한 결정을 후회하기 시작합니다. Chandra Patni 포인터를 시도하고 최대 절전 모드 delete_orphan 캐스케이드 유형을 사용합니다.
bert

이것에 대해 비슷한 질문이 있습니다. 이 게시물을 보시겠습니까? stackoverflow.com/questions/4569857/…
Thang Pham

77
JPA 2.0, 당신은 지금 orphanRemoval 사실 = 옵션을 사용할 수 있습니다
itsadok

2
고아 제거에 대한 훌륭한 초기 설명과 좋은 조언. JPA가 이러한 유형의 제거를 설명하지 않았다는 것을 몰랐습니다. 내가 최대 절전 모드에 대해 알고있는 것과 JPA가 실제로하는 것 사이의 미묘한 차이는 화를 낼 수 있습니다.
sma

컴포지션과 집계의 차이점을 공개하여 잘 설명했습니다!
Felipe Leão

73

클레 투스의 답변 외에도 2010 년 12 월 이후의 JPA 2.0orphanRemoval@OneToMany주석에 대한 속성을 소개합니다 . 자세한 내용은이 블로그 항목을 참조하십시오 .

스펙이 비교적 새롭기 때문에 모든 JPA 1 제공자가 최종 JPA 2 구현을 갖지는 않습니다. 예를 들어, Hibernate 3.5.0-Beta-2 릴리스 는 아직이 속성을 지원하지 않습니다.


블로그 항목-링크가 끊어졌습니다.
Steffi

1
그리고 뒤로 기계의 마법은 우리를 구원 : web.archive.org/web/20120225040254/http://javablog.co.uk/2009/...은
루이 Jacomet

43

당신은 이것을 시도 할 수 있습니다 :

@OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true).


2
감사. 그러나 질문은 JPA에서 1 번 돌아 왔습니다. 이 옵션은 이전보다 사용할 수 없었습니다.
bert

9
그럼에도 불구하고 JPA2 시대에 이것을위한 해결책을 찾는 우리들에게는 :)
alex440

20

설명했듯이 JPA로 원하는 것을 수행 할 수 없으므로 hibernate.cascade 주석을 사용하여 Parent 클래스의 관련 코드는 다음과 같습니다.

@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, mappedBy = "parent")
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,
            org.hibernate.annotations.CascadeType.DELETE,
            org.hibernate.annotations.CascadeType.MERGE,
            org.hibernate.annotations.CascadeType.PERSIST,
            org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
private Set<Child> childs = new HashSet<Child>();

부모도 삭제했기 때문에 'ALL'을 간단하게 사용할 수 없었습니다.


4

여기서 캐스케이드는 제거와 관련하여 부모를 제거하면 자식이 제거됨을 의미합니다. 협회가 아닙니다. JPA 제공자로 Hibernate를 사용하는 경우, hibernate specific cascade를 사용하여이를 수행 할 수 있습니다 .


4

당신은 이것을 시도 할 수 있습니다 :

@OneToOne(cascade = CascadeType.REFRESH) 

또는

@OneToMany(cascade = CascadeType.REFRESH)

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