クリアしたい@OneToManyコレクション(リスト)があり、同じトランザクションで新しい要素を追加します。
使用する
collection.clear();
collection.add(new EntityB());
新しいインスタンスを追加するだけで、何も削除しません。私が持っています orphanRemoval = true
(コレクションフィールド)。
追加:
// Parent entity
@OneToMany(mappedBy = "product", orphanRemoval = true)
private List<Feature> features = new ArrayList<>();
// Child entity
@ManyToOne(cascade = CascadeType.ALL)
private Product product;
// Clear and add attempt
product.getFeatures().clear();
Feature feature = new Feature(product, ls);
product.getFeatures().add(feature);
実際のソリューションは、mapedBy = ""パラメータの代わりに@JoinColumnアノテーションを使用していることがわかります。
双方向の関連付けの片側のみをクリアしようとします。
だから代わりに:
collection.clear();
この記事 で説明されているように、両側をクリアしてみてください。
for(Iterator<Feature> featureIterator = features.iterator();
featureIterator.hasNext(); ) {
Feature feature = featureIterator .next();
feature.setProduct(null);
featureIterator.remove();
}
また、@ManyToOne
からカスケードを削除し、@OneToMany
に移動します。
ただし、一意の制約がある場合、このclear + add
Anti-Patternは機能しません。これは、 この記事 で説明されているように、DELETEアクションの前にINSERTアクションが実行されるためです。
これを行う適切な方法は、削除する必要があるエントリを確認して、それらを削除することです。次に、新しいものを追加し、変更されたものを更新します。これは、コレクションを適切にマージする方法です。
これはHibernateの多くのバージョンのバグのようです。私はEclipseLinkでテストしましたが、問題なく動作します。
As Hibernateでの回避策(Hibernate 4.3.6-Finalでテスト済み):Feature
エンティティのカスケードを削除し、CascadeType.PERSIST
(またはCascadeType.ALL
)を追加しますProduct
エンティティ。
機能しないことを確認するために、次のことを試してください。
EntityManager em = ...//fetch the entitymanager. If a Container-managed transaction, you already got it injected
em.getTransaction().begin();//only if resource-local persistence unit. Otherwise if JTA: open the transaction the JTA-specific way (if that was not already done by the container)
Product product = em.find(Product.class, productId);
for (Feature crtFeature : product.getFeatures()) {
if (!em.contains(crtFeature)) {
throw new RuntimeException("Feature is not managed, so removeOrpahns cannot work");
}
}
product.getFeatures().clear();
Feature feature = new Feature(product, ls);
em.persist(feature);//you need this, as there is no cascading from Product to Feature.
product.getFeatures().add(feature);
em.getTransaction().commit();//if you work with a resource-local persistence unit. Otherwise if JTA: commit the transaction the JTA-specific way (if that was not already done by the container)
最近、同様の問題に直面しました。私にとっての問題は、孤児がまだ別の管理対象エンティティから参照されていて、その関係に対して定義されたPERSISTカスケードがあったことです。
// Parent entity
@OneToMany(mappedBy = "product", orphanRemoval = true)
private List<Feature> features = new ArrayList<>();
// Child entity
@ManyToOne
private Product product;
@ManyToOne
private Description description;
// Another entity (let's say descriptions can be shared between features)
@OneToMany(mappedBy = "description", cascade = CascadeType.PERSIST)
private List<Feature> features = new ArrayList<>();
関連するすべてのエンティティが管理されている、つまり永続化コンテキストにロードされていると仮定します。今度は、OPと同じことを行います。
// Clear and add attempt
product.getFeatures().clear();
Feature feature = new Feature(product, ls);
product.getFeatures().add(feature);
哲学的には、ここでの問題は、フィーチャーをProductエンティティからのみ削除し、Descriptionエンティティからは削除しない場合、オブジェクトモデルが不整合になることです。結局のところ、フィーチャーを削除したいのですが、それでも他のオブジェクトから参照されています。技術的には、Featureエンティティへの2つの競合するカスケードがあり、結果はそれらが適用される順序に依存する場合があります。
JPA 2.1仕様(2.9)に記載されているように、フィーチャーは製品のコレクションから削除されたため、孤立した削除が適用され、フィーチャーエンティティは次のフラッシュ中に「削除」状態に移行します。関連する部分を強調しました:
OneToOneまたはOneToManyとして指定された関連付けは、orphanRemovalオプションの使用をサポートします。 orphanRemovalが有効な場合、以下の動作が適用されます。
- リレーションシップのターゲットであるエンティティがリレーションシップから削除されると(リレーションシップをnullに設定するか、エンティティをリレーションシップコレクションから削除することにより)、孤立したエンティティに削除操作が適用されます。 削除操作はフラッシュ操作時に適用されます。 orphanRemoval機能は、親エンティティーが私的に「所有」しているエンティティーを対象としています。それ以外の場合、ポータブルアプリケーションは特定の削除順序に依存してはならず、必須ではない孤立したエンティティを別の関係に再割り当てするか、それ以外の場合は永続化しようとします。孤立しているエンティティが分離、新規、または削除されたエンティティである場合、orphanRemovalのセマンティクスは適用されません。
ただし、同じ機能は、機能に向かってカスケードするPERSISTを持つ説明エンティティから参照されます。 JPA 2.1仕様は次のように述べています。
エンティティXに適用されるフラッシュ操作のセマンティクスは次のとおりです。
Xが管理対象エンティティである場合、データベースに同期されます。
- Xからの関係によって参照されるすべてのエンティティYについて、Yへの関係にカスケード要素値のcascade = PERSISTまたはcascade = ALLで注釈が付けられている場合、永続化操作がYに適用されます。
したがって、このカスケードは、Descriptionでem.persist()を呼び出さなくても、Featureエンティティに対して「持続」操作を実行します。この永続的なカスケードをトリガーするフラッシュが実行されたときに、説明を管理することで十分です。
これは、仕様で指示されていないことを正確に実行していることを意味します-孤立した削除を実行し、同じエンティティで永続化します。 Hibernateで実際に起こっているように見えるのは、両方の操作が順番に適用されることです。最初に、削除操作によりフィーチャーエンティティが「削除済み」状態に移行し、次に永続操作により、削除されたエンティティが管理対象エンティティに戻ります。その結果、フィーチャーはデータベースから削除されません。
セクション2.9「エンティティの関係」で、JPA 2.1仕様は次のように述べています。
孤立したエンティティが、分離、新規、または削除されたエンティティである場合、orphanRemovalのセマンティクスは適用されません。
コレクションからエンティティを削除するときに、エンティティが永続コンテキストで管理されていることを確認しますか?
fetch=fetchType.EAGER
またはfetch joins
を使用して修正できます。または(ユースケースによって異なります)、適切なcascade
オプションを設定するだけで十分な場合があります。