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;

  ...
}

子セットから子を削除しても、データベースから削除されません。child.parent参照を無効にしてみましたが、それもうまくいきませんでした。

エンティティはWebアプリケーションで使用され、削除はAjaxリクエストの一部として行われます。保存ボタンを押したときに削除された子のリストがないため、暗黙的に削除することはできません。

回答:


252

JPAの動作は正しいです(つまり、仕様に従って)。OneToManyコレクションからオブジェクトを削除しただけでは、オブジェクトは削除されません。これを行うベンダー固有の拡張機能がありますが、ネイティブJPAはそれを提供しません。

これは、JPAがコレクションから削除されたものを削除する必要があるかどうかを実際に認識していないためです。オブジェクトモデリング用語では、これは合成と「集約」の違いです。

構成、子エンティティは親なくして存在していません。典型的な例は、家と部屋の間です。家を削除すると、部屋も移動します。

集約は緩い種類の関連付けであり、コースと学生に代表されます。コースを削除しても、学生がまだ存在している(おそらく他のコースにある)。

したがって、ベンダー固有の拡張機能を使用してこの動作を強制する(利用可能な場合)か、子を明示的に削除して、親のコレクションから削除する必要があります。

私は知っています:


素敵な説明に感謝します。だから私が恐れていたとおりです。(私が尋ねる前に検索/読書をしました、ただ保存したいだけです)。どういうわけか、JPA APIを使用してHibernateを直接nitするという決定を後悔し始めます..私はChandra Patniポインターを試して、hibernate delete_orphanカスケード型を使用します。
バートヤン

これについても同様の質問があります。こちらの投稿をご覧ください。stackoverflow.com/questions/4569857/...
タンファム

77
JPA 2.0を使用すると、今orphanRemoval = trueのオプションを使用することができます
itsadok

2
orphanRemovalに関する素晴らしい最初の説明と良いアドバイス。JPAがこの種の削除を考慮に入れていなかったという考えはありませんでした。Hibernateについて私が知っていることと、JPAが実際に行うことの間のニュアンスは、厄介なものになる場合があります。
sma

構成と集計の違いを明らかにすることでうまく説明されました!
フェリペLeão

73

cletusの回答に加えて、2010年12月以降の最終版であるJPA 2.0では、アノテーションにorphanRemoval属性が導入されています@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回戻ってきました。そして、このオプションは以前に利用できませんでした。
バート2013

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 固有のカスケードを使用して行うことができます。


4

あなたはこれを試すことができます:

@OneToOne(cascade = CascadeType.REFRESH) 

または

@OneToMany(cascade = CascadeType.REFRESH)

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