親エンティティと子エンティティ間の単純な@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リクエストの一部として行われます。保存ボタンが押されたときに削除された子のリストがないため、暗黙的に削除することはできません。
JPAの動作は正しい(つまり仕様に従って):OneToManyコレクションからオブジェクトを削除したからといって、オブジェクトは削除されません。それを行うベンダー固有の拡張機能がありますが、ネイティブJPAはこれに対応していません。
これは、JPAがコレクションから削除されたものを削除する必要があるかどうかを実際に知らないためです。オブジェクトモデリングの用語では、これはcompositionと "aggregation *"の違いです。
compositionでは、子エンティティは親なしでは存在しません。典型的な例は、家と部屋の間です。家を削除すると、部屋も行きます。
Aggregationはよりゆるい種類の関連付けであり、CourseとStudentに代表されます。コースを削除しても、学生は(おそらく他のコースに)存在します。
そのため、ベンダー固有の拡張機能を使用してこの動作を強制する(使用可能な場合)か、明示的に子を削除して親のコレクションから削除する必要があります。
私は知っています:
Cletusの答えに加えて、2010年12月以降の最終的な JPA 2. では、@OneToMany
アノテーションにorphanRemoval
属性が導入されています。詳細については、これを参照してください ブログエントリ 。
仕様は比較的新しいため、すべてのJPA 1プロバイダーが最終的なJPA 2実装を持つわけではないことに注意してください。たとえば、 Hibernate 3.5.0-Beta-2 release はまだこの属性をサポートしていません。
これを試すことができます:
@OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true)
。
説明したように、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」を単純に使用することはできませんでした。これにより、親も削除されてしまいます。
ここで、カスケードは、削除のコンテキストでは、親を削除すると子も削除されることを意味します。関連付けではありません。 JPAプロバイダーとしてHibernateを使用している場合、 hibernate specific cascade を使用して実行できます。
これを試すことができます:
@OneToOne(cascade = CascadeType.REFRESH)
または
@OneToMany(cascade = CascadeType.REFRESH)
@Entity
class Employee {
@OneToOne(orphanRemoval=true)
private Address address;
}
here を参照してください。