최대 절전 모드-cascade =”all-delete-orphan”을 가진 컬렉션이 더 이상 소유 엔티티 인스턴스에 의해 참조되지 않았습니다


225

엔티티를 업데이트하려고 할 때 다음과 같은 문제가 있습니다.

"A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance".

부모 엔터티가 있으며 Set<...>일부 자식 엔터티가 있습니다. 업데이트하려고하면 모든 참조 가이 컬렉션으로 설정되어 설정됩니다.

다음 코드는 내 매핑을 나타냅니다.

@OneToMany(mappedBy = "parentEntity", fetch = FetchType.EAGER)
@Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
public Set<ChildEntity> getChildren() {
    return this.children;
}

다음과 같이 Set <..> 만 청소하려고 시도했습니다. 문제를 "가능한"해결하는 방법 이 작동하지 않았습니다.

아이디어가 있으시면 알려주십시오.

감사!


1
@ mel3kings, 제공 한 링크가 더 이상 활성화되어 있지 않습니다.
오팔


요소를 제거 할 때 변경 가능한 컬렉션을 사용해보십시오. 예를 들어 something.manyother.remove(other)if manyother가 인 경우 사용하지 마십시오 List<T>. 많은 다른 ArrayList<T>orphanDelete = true
Mutable을

답변:


220

sonEntities에 무언가를 할당하는 모든 장소를 확인하십시오. 참조한 링크는 새 HashSet 작성을 명확하게 나타내지 만 세트를 다시 지정할 때마다이 오류가 발생할 수 있습니다. 예를 들면 다음과 같습니다.

public void setChildren(Set<SonEntity> aSet)
{
    this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
}

일반적으로 생성자에서 집합을 한 번만 "새"로 만들려고합니다. 목록에 무언가를 추가하거나 삭제할 때마다 새 목록을 할당하는 대신 목록의 내용을 수정해야합니다.

자녀를 추가하려면 :

public void addChild(SonEntity aSon)
{
    this.sonEntities.add(aSon);
}

어린이를 제거하려면 :

public void removeChild(SonEntity aSon)
{
    this.sonEntities.remove(aSon);
}

8
실제로 내 문제는 엔티티의 equals 및 hashcode에 관한 것입니다. 레거시 코드는 많은 문제를 일으킬 수 있으므로 반드시 확인하십시오. 내가 한 모든 것은 삭제 고아 전략을 유지하고 동등한 항목과 해시 코드를 수정하는 것입니다.
axcdnt

6
문제가 해결되어 다행입니다. equals와 hashcode가 Hibernate로 몇 번 물었습니다. "[Solved]"로 질문 제목을 업데이트하는 대신 계속해서 답변을 게시 한 다음 수락 된 답변으로 표시해야합니다.
brainimus

고마워, 나는 비슷한 것을 만났고 그 세트에 대한 내 세터가 비어있는 것으로 판명되었습니다.
Siddhartha

예, 빈 목록 에서도이 오류가 발생합니다. 고아가 없기 때문에 (목록이 비어 있기 때문에) 기대하지 않습니다.
tibi

4
일반적으로 생성자에서 집합을 한 번만 "새"로 만들려고합니다. 목록에 무언가를 추가하거나 삭제할 때마다 새 목록을 할당하는 대신 목록의 내용을 수정해야합니다. 대부분의 임프
Nikhil Sahu

109

방법:

public void setChildren(Set<SonEntity> aSet) {
    this.sonEntities = aSet;
}

parentEntity가 분리되면 업데이트하고 다시 업데이트하면 작동 합니다.
그러나 엔티티가 컨텍스트 당 분리되지 않으면 (즉, 찾기 및 업데이트 작업이 동일한 트랜잭션에 있음) 아래 방법이 작동합니다.

public void setChildren(Set<SonEntity> aSet) {
    //this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
    this.sonEntities.clear();
    if (aSet != null) {
        this.sonEntities.addAll(aSet);
    }
}

1
@ Skuld 비슷한 문제가 있으며 솔루션을 적용했습니다 (세터 메소드에서 자식 컬렉션-this.children.clear ()-새 자식-this.children.addAll (children)을 추가했습니다). 이 변경으로 문제가 해결되지 않았습니다. 여전히 "cascade ="all-delete-orphan "을 가진 컬렉션이 더 이상 소유 엔티티 인스턴스에 의해 참조되지 않았습니다"예외가 발생합니다. 왜 그런지 알아? 대단히 감사합니다!
ovdsrn

@ovdsrn 죄송합니다. 답변이 아닙니다. 답변의 형식을 정렬했습니다. 원저자 (kmmanu)가 도움을 줄 수도 있습니다 (또는 시나리오가 다음과 다른 경우 새 질문을 시작하고 싶을 수도 있습니다). 행운을 빌어 요
Skuld

1
이것은 잘 작동하지만 자식이 중첩 된 최대 절전 모드 구성 요소에 포함되어 있고 구성 요소가 Entity에서 null로 설정된 경우에는 좋지 않습니다. 그런 다음 동일한 오류가 발생합니다. 이것은 자식의 소유자가 루트 엔터티이고 null로 만들어진 구성 요소가 아니기 때문입니다. 이러한 구성 요소는 null이 될 수 없지만 대신에 destroy () 메소드로 전달되어야합니다. child ... 적어도, 나는 더 나은 해결책을 모른다 ... 그것은 일종의 collection clear () 구성이지만 destroy ()를 통해 구성 요소에 대해 ...
edbras

위의 자세한 내용은이 게시물을 참조하십시오. stackoverflow.com/questions/4770262/…
edbras

7
aSet == this.sonEntities (예 : 동일한 객체) 인 경우 추가하기 전에 세트를 지우므로 sonEntities.clear () 대신 this.sonEntities.retainAll (aSet)을 사용하십시오!
Martin

33

최대 절전 모드에서 컬렉션에 할당하는 것을 좋아하지 않는 다양한 장소에서 읽을 때 가장 안전한 방법은 다음과 같이 최종적으로 만드는 것입니다.

class User {
  private final Set<Role> roles = new HashSet<>();

public void setRoles(Set<Role> roles) {
  this.roles.retainAll(roles);
  this.roles.addAll(roles);
}
}

그러나 이것은 작동하지 않으며, "더 이상 참조되지 않음"오류가 발생하며이 경우 실제로 오해의 소지가 있습니다.

최대 절전 모드가 setRoles 메서드를 호출하고 여기에 특수 컬렉션 클래스를 설치하려고하지만 컬렉션 클래스를 허용하지 않습니다. set 메소드에서 컬렉션에 할당하지 않는 것에 대한 모든 경고를 읽었음에도 불구하고 오랫동안 오랜 시간 동안 혼란에 빠졌습니다.

그래서 나는 이것을 바꿨다.

public class User {
  private Set<Role> roles = null;

  public void setRoles(Set<Role> roles) {
  if (this.roles == null) {
    this.roles = roles;
  } else {
    this.roles.retainAll(roles);
   this.roles.addAll(roles);
  }
}
}

따라서 첫 번째 호출에서 최대 절전 모드는 특수 클래스를 설치하고 후속 호출에서는 모든 것을 손상시키지 않고 메소드를 직접 사용할 수 있습니다. 클래스를 Bean으로 사용하려면 작동중인 setter가 필요할 수 있으며 적어도 작동하는 것 같습니다.


2
왜 retainAll ()을 호출합니까? 왜 addAll () 다음에 clear ()를 사용하지 않습니까?
edbras

1
"왜 retainAll ()을 호출합니까? 왜 clear () 다음에 addAll ()을 사용하지 않습니까?" 좋은 질문입니다. 항목을 다시 추가하기 전에 제거하지 않으면 최대 절전 모드에서 데이터베이스 업데이트로 볼 가능성이 적다는 가정하에 작업하고 있다고 생각합니다. 그러나 어쨌든 그렇게 작동하지 않는 것 같습니다.
xpusostomos

2
clear () 대신 retainAll ()을 사용해야합니다. 그렇지 않으면 동일한 세트 객체를 전달할 경우 역할을 지울 수 있습니다. 예 : user.setRoles (user.getRoles ()) == user.roles.clear ()
Martin

2
orphanRemoval = true를 설정하고이 모음이 null 인 레코드를 만들면이 오류도 발생합니다. 따라서 a에는 orphanremoval = true 인 oneToMany b가 있습니다. B = null 인 A를 만들면이 문제가 발생합니다. 초기화하고 최종적으로 만드는 솔루션이 가장 좋습니다.
로렌스

감사합니다! 로 내 목록을 초기화하고있었습니다 List<String> list = new ArrayList<>();. List<String> list = null;문제를 해결 하기 위해 변경 :)
Radical

19

실제로 내 문제는 엔티티의 equals 및 hashcode에 관한 것입니다. 레거시 코드는 많은 문제를 일으킬 수 있으므로 반드시 확인하십시오. 내가 한 모든 것은 삭제 고아 전략을 유지하고 동등한 항목과 해시 코드를 수정하는 것입니다.


9
잘 만들어진 equals 및 hashCode 메소드의 예를 참조하십시오. 많은 문제가 있기 때문에 : 또는 세트를 업데이트 할 수 없거나 StackOverflow 오류가 발생합니다. 나는 여기에 질문을 열 : stackoverflow.com/questions/24737145/...를 . 감사합니다
SaganTheBest

11

나는 같은 오류가 있었다. 나에게 문제는 엔터티를 저장 한 후 매핑 된 컬렉션이 여전히 null이고 엔터티를 업데이트하려고 할 때 예외가 발생한다는 것입니다. 나에게 도움이 된 것 : 엔티티를 저장하고 새로 고침 (컬렉션이 더 이상 null이 아님)을 한 다음 업데이트를 수행하십시오. 새로운 ArrayList ()로 컬렉션을 초기화하거나 도움이 될 수도 있습니다.


null 대신 새 ArrayList를 보내면 나를 위해 일했습니다. 감사합니다
rpajaziti

4

관계 유형 :


에 선언 된 컬렉션을 인스턴스화하지 말고 hasMany개체를 추가하고 제거하십시오.

class Parent {
    static hasMany = [childs:Child]
}

사용 관계 유형 :


그러나 컬렉션이 속성 (사용 관계)으로 선언되고 선언시 초기화되지 않은 경우에만 컬렉션이 null 일 수 있습니다.

class Parent {
    List<Child> childs = []
}

4

@ user2709454 접근 방식을 약간 개선하여 사용했습니다.

public class User {
    private Set<Role> roles;

    public void setRoles(Set<Role> roles) {
        if (this.roles == null) {
            this.roles = roles;
        } else if(this.roles != roles) { // not the same instance, in other case we can get ConcurrentModificationException from hibernate AbstractPersistentCollection
            this.roles.clear();
            if(roles != null){
                this.roles.addAll(roles);
            }
        }
    }
}

3

사용하려고 할 때이 문제가 발생했습니다 TreeSet. 나는 초기화 한 oneToMany함께 TreeSet하는 작품

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private Set<WizardAnswer> answers = new TreeSet<WizardAnswer>();

그러나 이렇게하면 question위에서 설명한 오류가 발생합니다 . 따라서 hibernate지원되는 것으로 보이며 SortedSet위의 줄을 다음과 같이 변경하면

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private SortedSet<WizardAnswer> answers;

그것은 마술처럼 작동합니다 :) 자세한 정보는 여기hibernate SortedSet있습니다.


아마도, Collections.emptySet () 사용자가 싱글 톤 빈리스트를 사용하는 것이 좋습니다
ryzhman

3

이 오류가 발생하는 유일한 시간은 컬렉션의 setter에 NULL을 전달하려고 할 때입니다. 이를 방지하기 위해 세터는 다음과 같습니다.

public void setSubmittedForms(Set<SubmittedFormEntity> submittedForms) {
    if(submittedForms == null) {
        this.submittedForms.clear();
    }
    else {
        this.submittedForms = submittedForms;
    }
}

3

JSON 게시 요청으로 엔티티를 업데이트 할 때이 문제가 발생했습니다. 하위 항목이없는 경우에도 하위에 대한 데이터없이 엔티티를 업데이트 할 때 오류가 발생했습니다. 첨가

"children": [],

요청 본문에 문제가 해결되었습니다.


1

또 다른 원인은 롬복을 사용하는 것일 수 있습니다.

@Builder- Collections.emptyList()당신이 말하더라도 저장하는 원인.myCollection(new ArrayList());

@Singular-클래스 수준 기본값을 무시하고 클래스 null필드가 다음과 같이 선언 된 경우에도 필드를 그대로 둡니다.myCollection = new ArrayList()

내 2 센트, 방금 같은 시간으로 2 시간을 보냈습니다.



1

Spring Boot를 사용하고 있으며 직접 덮어 쓰지 않아도 컬렉션과 관련 하여이 문제가 발생했습니다. 왜냐하면 동일한 프런트 엔드 친화적 인 표현을 제공하기 위해 사용자 정의 serializer 및 deserializer 로 동일한 컬렉션에 대한 추가 필드를 선언하고 있기 때문 입니다. 데이터:

  public List<Attribute> getAttributes() {
    return attributes;
  }

  public void setAttributes(List<Attribute> attributes) {
    this.attributes = attributes;
  }

  @JsonSerialize(using = AttributeSerializer.class)
  public List<Attribute> getAttributesList() {
    return attributes;
  }

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes = attributes;
  }

컬렉션을 직접 덮어 쓰지 않더라도 deserialization 은이 문제를 모두 동일하게 트리거하는 후드에서 수행합니다. 해결책은 deserializer와 관련된 setter를 변경하여 목록을 지우지 않고 목록을 지우고 모든 것을 추가하는 것입니다.

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes.clear();
    this.attributes.addAll(attributes);
  }

1

나는 같은 문제가 있었지만 세트가 null 일 때였습니다. 목록의 모음집 설정 작업에서만 찾을 수 있습니다. JPA 주석 fetch = FetchType.EAGER의 intelted @LazyCollection (LazyCollectionOption.FALSE) 최대 절전 모드 주석을 사용해보십시오.

내 솔루션 : 이것은 내 구성이며 잘 작동합니다.

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private Set<Barcode> barcodes;

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private List<FormatAdditional> additionals;

1
@OneToMany(mappedBy = 'parent', cascade= CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>();

자식 개체를 기존 자식 개체 목록에 추가 할 때도 같은 오류가 발생했습니다.

childService.saveOrUpdate(child);
parent.addToChildren(child);
parentService.saveOrUpdate(parent);

내 문제를 해결 한 것은 다음으로 변경됩니다.

child = childService.saveOrUpdate(child);

이제 아이는 다른 세부 사항으로 부활하고 잘 작동했습니다.


0

내 멍청한 대답 추가. 우리는 Spring Data Rest를 사용하고 있습니다. 이것은 우리의 표준 관계였습니다. 이 패턴은 다른 곳에서 사용되었습니다.

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
List<Child> children = new LinkedList<>()


//Child class
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = 'ParentID', updatable = false)
@JsonBackReference
Parent parent

우리가 만든 관계로, 항상 아이들이 자신의 저장소를 통해 추가되도록 의도되었습니다. 아직 레포를 추가하지 않았습니다. 우리가 통합 테스트는 REST 호출을 통해 엔터티의 전체 수명주기를 거쳤으므로 요청 사이에 트랜잭션이 닫힙니다. 자식에 대한 repo가 ​​없다는 것은 json이에서 대신에 주요 구조의 일부로 자식을 가졌음을 의미했습니다 _embedded. 그러면 부모 업데이트로 인해 문제가 발생할 수 있습니다.


0

다음 솔루션이 나를 위해 일했습니다.

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@OrderBy(value="ordinal ASC")
List<Child> children = new ArrayList<>()

//Updated setter of children 
public void setChildren(List<Children> children) {
    this.children.addAll(children);
    for (Children child: children)
        child.setParent(this);
}


//Child class
@ManyToOne
@JoinColumn(name="Parent_ID")
private Parent parent;

0

새로운 컬렉션을 할당하는 대신

public void setChildren(Set<ChildEntity> children) {
    this.children = children;
}

모든 요소를

public void setChildren(Set<ChildEntity> children) {
    Collections.replaceAll(this.children,children);
}

0

조심해

BeanUtils.copyProperties(newInsum, insumOld,"code");

이 방법도 최대 절전 모드를 해제합니다.



0

Spring Boot와는 완전히 다릅니다! 나에게 그것은 컬렉션 속성을 설정했기 때문이 아닙니다.

내 테스트에서 엔터티를 만들려고했지만 사용하지 않은 다른 컬렉션에 대해이 오류가 발생했습니다!

많은 노력을 기울인 후 @Transactional테스트 방법을 추가하여 해결했습니다. 그래도 이유는 없습니다.


0

이것은 이전 답변과 달리 정확히 같은 오류가 발생했습니다. setter 함수가 다음과 같이 표시 될 때 "cascade ="all-delete-orphan "모음이 더 이상 참조되지 않았습니다 ..."

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    if( this.taxCalculationRules == null ) {
        this.taxCalculationRules = taxCalculationRules_;
    } else {
        this.taxCalculationRules.retainAll(taxCalculationRules_);
        this.taxCalculationRules.addAll(taxCalculationRules_);
    }
}

그런 다음 간단한 버전으로 변경하면 사라졌습니다.

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    this.taxCalculationRules = taxCalculationRules_;
}

(최대 절전 모드-5.4.10 및 4.3.11을 모두 시도했습니다. setter의 간단한 할당으로 돌아 가기 전에 며칠 동안 모든 종류의 솔루션을 사용해 보았습니다. 왜 그렇게되는지 혼란 스럽습니다.)

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