JPA2.0orphanRemoval
属性について少し混乱しています。
ON DELETE CASCADE
JPAプロバイダーのDB生成ツールを使用して、特定の関係を持つ基盤となるデータベースDDLを作成するときに、これが必要であることがわかると思います。
ただし、DBが存在し、すでにON DELETE CASCADE
関係にある場合、これは削除を適切にカスケードするのに十分ではありませんか?さらに何orphanRemoval
をしますか?
乾杯
JPA2.0orphanRemoval
属性について少し混乱しています。
ON DELETE CASCADE
JPAプロバイダーのDB生成ツールを使用して、特定の関係を持つ基盤となるデータベースDDLを作成するときに、これが必要であることがわかると思います。
ただし、DBが存在し、すでにON DELETE CASCADE
関係にある場合、これは削除を適切にカスケードするのに十分ではありませんか?さらに何orphanRemoval
をしますか?
乾杯
orphanRemoval
とは何の関係もありませんON DELETE CASCADE
。
orphanRemoval
完全にORM固有のものです。これは、「親」エンティティから参照されなくなったとき、たとえば、親エンティティの対応するコレクションから子エンティティを削除したときに、「子」エンティティを削除するようにマークします。
ON DELETE CASCADE
はデータベース固有のものであり、「親」行が削除されると、データベースの「子」行が削除されます。
ここにある例:
Employee
エンティティオブジェクトが削除されると、削除操作は参照されているエンティティオブジェクトにカスケードされますAddress
。この点で、orphanRemoval=true
とcascade=CascadeType.REMOVE
は同一であり、orphanRemoval=true
指定されている場合CascadeType.REMOVE
は冗長です。
2つの設定の違いは、関係の切断に対する応答にあります。たとえば、アドレスフィールドnull
を別のAddress
オブジェクトに設定する場合などです。
が指定されている場合orphanRemoval=true
、切断されたAddress
インスタンスは自動的に削除されます。Address
これは、所有者オブジェクト(eg)からの参照なしでは存在してはならない依存オブジェクト(eg)をクリーンアップするのに役立ちますEmployee
。
のみcascade=CascadeType.REMOVE
が指定されている場合、関係の切断は削除操作ではないため、自動アクションは実行されません。
孤立した削除の結果として参照がぶら下がるのを防ぐために、この機能は、共有されていないプライベートな依存オブジェクトを保持するフィールドに対してのみ有効にする必要があります。
これがより明確になることを願っています。
コレクションから子エンティティを削除すると、その子エンティティもDBから削除されます。orphanRemovalは、親を変更できないことも意味します。従業員がいる部門がある場合、その従業員を削除して別の部門に配置すると、フラッシュ/コミット(どちらか早い方)でその従業員を誤ってDBから削除してしまいます。士気は、その親の子がその存在を通じて別の親に移行しないことが確実である限り、orphanRemovalをtrueに設定することです。orphanRemovalをオンにすると、カスケードリストにREMOVEが自動的に追加されます。
JPAは、エンティティの状態遷移をINSERT、UPDATE、DELETEなどのSQLステートメントに変換します。
エンティティの場合、自動または手動でフラッシュpersist
されたときに実行されるINSERTステートメントをスケジュールしています。EntityManager
エンティティの場合remove
、永続コンテキストがフラッシュされたときに実行されるDELETEステートメントをスケジュールしています。
便宜上、JPAを使用すると、エンティティの状態遷移を親エンティティから子エンティティに伝播できます。
したがって、子エンティティと関連付けられPost
ている親エンティティがある場合は、次のようになります。@OneToMany
PostComment
エンティティ内のcomments
コレクションはPost
次のようにマップされます。
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Comment> comments = new ArrayList<>();
この属性は、JPAプロバイダーに、親エンティティからコレクションに含まれるすべてのエンティティcascade
へのエンティティ状態遷移を渡すように指示します。Post
PostComment
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
と、JPAプロバイダーはremove
、子エンティティがコレクションから削除されたときに操作をスケジュールします。
したがって、私たちの場合、
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);
エンティティがコレクションで参照されなくなったため、JPAプロバイダーは関連するpost_comment
レコードを削除します。PostComment
comments
DELETE FROM post_comment WHERE id = 1
はON DELETE CASCADE
FKレベルで定義されています。
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
エンティティは、データベースエンジンによって自動的に削除されます。ただし、誤ってルートエンティティを削除すると、これは非常に危険な操作になる可能性があります。
JPAcascade
とorphanRemoval
オプションの利点は、更新の損失を防ぐために楽観的ロックの恩恵を受けることもできることです。
JPAカスケードメカニズムを使用する場合、DDLレベルを使用する必要はありませんON DELETE CASCADE
。これは、複数のレベルに多数の子エンティティを持つルートエンティティを削除する場合に非常に危険な操作になる可能性があります。
DDLの同等のJPAマッピングON DELETE CASCADE
はですcascade=CascadeType.REMOVE
。孤立した削除とは、「親」エンティティとの関係が破棄されたときに、依存エンティティが削除されることを意味します。たとえば@OneToMany
、エンティティマネージャで子を明示的に削除せずに、子をリレーションシップから削除した場合です。
違いは次のとおり
です。--orphanRemoval=true:「子」エンティティは、参照されなくなると削除されます(親は削除されない場合があります)。
--CascadeType.REMOVE:「子」エンティティは、その「親」が削除された場合にのみ削除されます。
orphanRemoval = true
@GaryKの答えは絶対に素晴らしいです、私は説明を探すのに1時間費やしました、CascadeType.REMOVE
そしてそれは私が理解するのを助けました。
要約:オブジェクト()を削除し、子オブジェクトも削除する場合にのみorphanRemoval = true
、同じように機能します。CascadeType.REMOVE
entityManager.delete(object)
まったく異なる状況で、のようなデータをフェッチList<Child> childs = object.getChilds()
してから子(entityManager.remove(childs.get(0)
)を削除orphanRemoval=true
すると、に対応するエンティティchilds.get(0)
がデータベースから削除されます。
孤立した削除は、次のシナリオで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)に次のように保存されます。
学生テーブル:-
1ロイECE1
2ニックECE1
1ヨハネ$1500
そして今、私が学生の1人を削除したい場合は、
Student student1 = em.find(Student.class,1);
em.remove(student1);
学生レコードが削除されると、対応するガイドレコードも削除する必要があります。ここで、StudentエンティティのCASCADE.REMOVE属性が表示され、識別子1の学生と対応するガイドオブジェクト(識別子)が削除されます。 1)。ただし、この例では、同じガイドレコードにマップされているもう1つの学生オブジェクトがあり、ガイドエンティティでorphanRemoval = true属性を使用しない限り、上記の削除コードは機能しません。