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 CASCADEあるデータベース固有のものは、「親」の行が削除されると、それはデータベースに「子」の行を削除します。


3
これはそれらに安全な効果があることを意味しますが、別のシステムがそれを起こさせる責任がありますか?
Anonymoose 2012

101
アノン、同じ効果はありません。ON DELETE CASCADEは、親が削除されたときにすべての子レコードを削除するようにDBに指示します。つまり、INVOICEを削除してから、そのINVOICEのすべてのアイテムを削除します。OrphanRemovalは、(メモリ操作で)Invoiceオブジェクトに属するItemのコレクションからItemオブジェクトを削除し、次に「保存」すると、削除されたItemが基になるDBから削除されることをORMに伝えます。
garyKeorkunian

2
単方向の関係を使用する場合、orphanRemoval = trueを設定しなくても、孤立は自動的に削除されます
Tim

98

ここでフォームを使用した例:

場合Employeeエンティティオブジェクトが削除され、削除操作は、参照にカスケード接続されたAddressエンティティ・オブジェクト。この点でorphanRemoval=truecascade=CascadeType.REMOVEは同一であり、orphanRemoval=true指定されている場合CascadeType.REMOVEは冗長です。

2つの設定の違いは、関係の切断に対する応答です。たとえば、住所フィールドnullを別のAddressオブジェクトに設定する場合などです。

  • orphanRemoval=trueが指定されている場合、切断されたAddressインスタンスは自動的に削除されます。これはAddress、所有者オブジェクト(などEmployee)からの参照なしでは存在してはならない依存オブジェクト(など)をクリーンアップするのに役立ちます。

  • のみcascade=CascadeType.REMOVEを指定した場合、関係の切断は削除操作ではないため、自動アクションは実行されません。

孤立した削除の結果として参照がぶら下がるのを避けるため、この機能は、非共有の非依存のプライベートオブジェクトを保持するフィールドに対してのみ有効にする必要があります。

これがより明確になることを願っています。


あなたの答えを読んだ後、私はそれらの両方と私の問題の正確な違いが解決されたことに気づきました。子エンティティが親エンティティの定義済みコレクションから切断(削除)されている場合、子エンティティをデータベースから削除するのに行き詰まりました。同じことについて、「stackoverflow.com/questions/15526440/…」という質問をしました。コメントを追加して、両方の質問をリンクします。
Narendra Verma 2013年


46

コレクションから子エンティティを削除すると、DBからもその子エンティティが削除されます。orphanRemovalは、親を変更できないことも意味します。従業員がいる部門がある場合、その従業員を削除して別の部門に配置すると、flush / commit(どちらが先か)でその従業員をDBからうっかり削除してしまいます。その親の子がその存在を通じて別の親に移行しないことが確実である限り、orphanRemovalをtrueに設定することが士気です。orphanRemovalをオンにすると、カスケードリストにREMOVEも自動的に追加されます。


3
完全に正しい...「プライベート」な親子関係とも呼ばれます。
HDave

つまりdepartment.remove(emp);、その従業員を呼び出すとすぐに、呼び出しさえせずにempテーブルから削除されますcommit()
JavaTechnical

18

DDL ON DELETE CASCADEと同等のJPAマッピングはcascade=CascadeType.REMOVEです。孤立した削除とは、「親」エンティティとの関係が破壊されると、依存エンティティが削除されることを意味します。たとえば@OneToMany、エンティティマネージャで明示的に削除せずに子を関係から削除した場合などです。


1
cascade=CascadeType.REMOVEはと同等ではありませんON DELETE CASCADE。オンにすると、アプリケーションコードで削除され、DDLには影響しません。その他はDBで実行されます。stackoverflow.com/a/19696859/548473を
Grigory Kislin 2017年

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属性は、エンティティの状態遷移を親PostエンティティからコレクションにPostComment含まれるすべてのエンティティに渡すようにJPAプロバイダーに指示します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属性をtrueに設定するとremove、子エンティティがコレクションから削除されたときに、JPAプロバイダーが操作をスケジュールします。

したがって、私たちの場合、

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

エンティティがコレクションで参照されなくなっpost_commentたため、JPAプロバイダーは関連するレコードを削除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側で削除がない場合、2つのエンティティ間の接続の削除はDBに保持されませんか?
aurelije

孤立した削除はの影響を受けませんCascadeType。それは補完的なメカニズムです。今、あなたは削除を永続化と間違えています。孤立した削除とは、参照されていない関連付けを削除することであり、永続化は新しいエンティティを保存することです。これらの概念をよりよく理解するには、回答に記載されているリンクをたどる必要があります。
Vlad Mihalcea

わからないことが1つあります。M側の接続を削除しない場合、孤立マッピングは双方向マッピングでどのように実行されますか?PostComment.postをnullに設定せずにPostCommentをPostのリストから削除しても、DB内のこれら2つのエンティティ間の接続は削除されないと思います。ですから、私は孤児の除去が効かないと思います。リレーショナルな世界では、PostCommentは孤児ではありません。時間があるときにテストします。
aurelije

1
これらの2つの例を私のハイパフォーマンスJava Persistence GitHubリポジトリーに追加しました。通常はエンティティを直接削除する必要があるため、子側を同期する必要はありません。ただし、孤立した削除はカスケードが追加された場合にのみ機能しますが、これはHibernateの制限であり、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
2番目の段落にタイプミスがあります。entityManager.delete(obj)などのメソッドはありません。これはentityManager.remove(obj)です。
JL_SO 2018

3

孤立した削除は、次のシナリオではON DELETE CASCADEと同じ効果があります。-生徒エンティティとガイドエンティティの間に単純な多対1の関係があり、多くの生徒を同じガイドにマッピングして、データベースにStudentテーブルがFKとしてid_guideを持つような、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);

ここでは、同じガイドを2つの異なる生徒オブジェクトにマッピングしています。CASCADE.PERSISTが使用されているため、オブジェクトグラフは以下のようにデータベーステーブルに保存されます(私の場合はMySql)。

学生テーブル:-

ID名部門ID_ガイド

1ロイECE 1

2ニックECE 1

ガイド表:-

ID NAME給与

1ジョン$ 1500

そして、私が生徒の一人を削除したい場合は、

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

生徒のレコードが削除されると、対応するガイドレコードも削除されます。この場合、生徒エンティティのCASCADE.REMOVE属性が機能し、何が行われるかがわかります。これにより、識別子1の生徒と対応するガイドオブジェクト(識別子)が削除されます。 1)。ただし、この例では、同じガイドレコードにマップされるもう1つの生徒オブジェクトがあり、Guide EntityでorphanRemoval = true属性を使用しない限り、上記の削除コードは機能しません。

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