Hibernate-カスケード=” 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
nurettin

回答:


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
実際、私の問題は、エンティティの等号とハッシュコードに関するものでした。レガシーコードは多くの問題を引き起こす可能性があります。チェックすることを忘れないでください。私がやったことは、単に孤立した削除の戦略を維持し、等号とハッシュコードを修正することだけでした。
axcdnt

6
問題を解決してよかったです。等号とハッシュコードがHibernateで何度か私を噛みました。質問のタイトルを「[解決済み]」に更新する代わりに、先に進んで回答を投稿し、承認済みの回答としてマークする必要があります。
brainimus

おかげで、私は似たように走ったとSetが空だったこと..ための私のセッターを判明
シッダールタ

はい、空のリストでもこのエラーが発生します。孤児がいないので(リストは空でした)、私はそれを期待しません。
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
@同様の問題が発生し、解決策を適用しました(setterメソッドで、子コレクション-this.children.clear()をクリアし、新しい子-this.children.addAll(children)を追加しました)。この変更で問題は解決しませんでした。「cascade = "all-delete-orphanを含むコレクション"は所有エンティティインスタンスによって参照されなくなった」という例外が引き続き発生します。なぜか分かりますか?どうもありがとうございました!
ovdsrn

@ovdsrn申し訳ありませんが、これは私の答えではありません。私は答えのフォーマットを整理しただけです。元の作者(kmmanu)があなたを助けることができるかもしれません(または、シナリオが異なる場合は、新しい質問を始めることもできます)元の質問はここで尋ねました)幸運
Skuld

1
これはうまく機能しますが、ネストされた休止状態のコンポーネントに子が含まれていて、そのエンティティでコンポーネントがnullにされている場合は、あまり良くありません。その後、同じエラーが発生します。これは、子の所有者がルートエンティティであり、nullにされるコンポーネントではないためです。そのため、コンポーネントがnullになることは決して許可されず、むしろ、結果としてdestroy()メソッドに転送されるはずです。子...少なくとも、より良い解決策はわかりません...これは一種のコレクションのclear()構成ですが、destroy()を介してコンポーネントに適用されます...
edbras 14

上記の詳細については、この記事を参照:stackoverflow.com/questions/4770262/...
edbras

7
sonEntities.clear()ではなくthis.sonEntities.retainAll(aSet)を使用します。aSet== this.sonEntities(つまり、同じオブジェクト)の場合、セットに追加する前にセットをクリアするからです!
マーティン

33

hibernateがコレクションへの割り当てを望まないさまざまな場所を読んだとき、私は最も安全なことは明らかに次のようにそれをfinalにすることだと思いました:

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

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

ただし、これは機能せず、恐ろしい「参照されなくなった」エラーが表示されます。これは、この場合、実際には非常に誤解を招くものです。

hibernateが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);
  }
}
}

そのため、最初の呼び出しでhibernateは特別なクラスをインストールし、その後の呼び出しでは、すべてを壊すことなく自分でメソッドを使用できます。クラスをBeanとして使用する場合は、おそらく機能するセッターが必要ですが、これは少なくとも機能するようです。


2
なぜretainAll()を呼び出すのですか?clear()の後にaddAll()を続けないのはなぜですか?
edbras 2014

1
「retainAll()を呼び出すのはなぜですか?clear()の後にaddAll()を呼び出さないのはなぜですか?」良い質問ですが、アイテムを再度追加する前にアイテムを削除しないと、Hibernateがこれをデータベースの更新と見なす可能性が低くなるという仮定の下で作業していたと思います。しかし、とにかくそれはそのように機能しないと思います。
xpusostomos 2015年

2
clear()よりもretainAll()を使用する必要があります。そうしないと、同じセットオブジェクトを渡した場合にロールが消去される可能性があります。例:user.setRoles(user.getRoles())== user.roles.clear()
Martin

2
orphanRemoval = trueを設定し、このコレクションがnullであるレコードを作成すると、このエラーも発生します。つまり、aにはoneToMany bがあり、orphanremoval = trueです。B = nullのAを作成すると、この問題が発生します。初期化して最終的にするためのソリューションが最良のようです。
ローレンス

ありがとうございました!リストをとして初期化していましたList<String> list = new ArrayList<>();List<String> list = null;問題を修正するためにそれを変更する:)
過激な

19

実際、私の問題は、エンティティの等号とハッシュコードに関するものでした。レガシーコードは多くの問題を引き起こす可能性があります。チェックすることを忘れないでください。私がやったことは、単に孤立した削除の戦略を維持し、等号とハッシュコードを修正することだけでした。


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

このエラーが発生するのは、コレクションのセッターにNULLを渡そうとしたときだけです。これを防ぐために、私のセッターは次のようになります。

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

3

JSON postリクエストでエンティティを更新するときに、これに遭遇しました。エンティティがない場合でも、子に関するデータなしでエンティティを更新すると、エラーが発生しました。追加

"children": [],

リクエスト本文に問題を解決しました。


1

もう1つの原因は、ロンボクの使用です。

@Builder- Collections.emptyList()あなたが言っても保存する原因.myCollection(new ArrayList());

@Singular- nullクラスフィールドが次のように宣言されている場合でも、クラスレベルのデフォルトを無視してフィールドを残しますmyCollection = new ArrayList()

私の2セントは、同じものを2時間費やしました:)


1

私がA collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance設定していたときに私は得ていましたparent.setChildren(new ArrayList<>())。に変更するparent.getChildren().clear()と問題は解決しました。

詳細を確認してください:HibernateException -cascade = "all-delete-orphan"を含むコレクションは、所有エンティティインスタンスによって参照されなくなりました


1

Spring Bootを使用していて、直接コレクションを上書きしないにもかかわらず、コレクションにこの問題がありました。フロントエンドフレンドリーな表現を提供するために、カスタムシリアライザーとデシリアライザーを使用して同じコレクションの追加フィールドを宣言しています。データ:

  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;
  }

自分でコレクションを上書きしているわけではありませんが、内部で逆シリアル化を行っているため、この問題はまったく同じです。解決策は、デシリアライザに関連付けられたセッターを変更して、リストを上書きするのではなく、リストをクリアしてすべてを追加することでした。

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

1

私は同じ問題を抱えていましたが、それはセットがnullのときでした。リスト作業のセットコレクションでのみ検索します。JPAアノテーションfetch = FetchType.EAGERの代わりに、休止状態アノテーション@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呼び出しを介してエンティティの完全なライフサイクルを通過するため、リクエスト間でトランザクションが閉じます。子のレポがないため、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

スプリングブーツとはまったく違います。私にとっては、コレクションプロパティの設定によるものではありませんでした。

私のテストでは、エンティティを作成しようとして、未使用の別のコレクションでこのエラーが発生しました!

何度も試した後@Transactional、テストメソッドにを追加しただけで解決しました。理由はありませんが。


0

これは以前の回答とは対照的ですが、セッター関数が次のようになっていると、まったく同じエラーが発生しました。

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の両方を試しました。セッターでの単純な割り当てに戻る前に、あらゆる種類のソリューションを数日間試してみました。なぜこれがそうなのか混乱しています。)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.