wake-up-neo.com

JPA Sammlung löschen und neue Elemente hinzufügen

Ich habe eine @OneToMany-Sammlung (Liste), die ich löschen und neue Elemente in derselben Transaktion hinzufügen möchte.

Verwenden 

collection.clear();
collection.add(new EntityB());

Fügt einfach die neue Instanz hinzu und entfernt niemals etwas. Ich habe orphanRemoval = true für das Sammlungsfeld.

HINZUGEFÜGT:

// 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);
13
Dennis Thrysøe

Es stellt sich heraus, dass die eigentliche Lösung eine @JoinColumn-Annotation anstelle des Parameters mappedBy = "" verwendet hat.

3
Dennis Thrysøe

Sie versuchen, nur eine Seite der bidirektionalen Zuordnung zu löschen.

Also statt:

collection.clear();

Wie in diesem Artikel erklärt, versuchen Sie, beide Seiten zu löschen, und es sollte funktionieren:

for(Iterator<Feature> featureIterator = features.iterator(); 
    featureIterator.hasNext(); ) {
    Feature feature = featureIterator .next();
    feature.setProduct(null);
    featureIterator.remove();
}

Entfernen Sie auch die Kaskade aus @ManyToOne und verschieben Sie sie nach @OneToMany.

Beachten Sie die einzigartigen Einschränkungen

Wenn Sie jedoch eine eindeutige Einschränkung haben, funktioniert clear + add Anti-Pattern nicht, da die INSERT-Aktion vor der DELETE-Aktion ausgeführt wird, wie in diesem Artikel erläutert.

Der richtige Weg, dies zu tun, ist zu prüfen, welche Einträge entfernt werden müssen, und diese einfach entfernen. Fügen Sie dann die neuen hinzu und aktualisieren Sie die, die geändert wurden. So führen Sie eine Zusammenführung der Sammlung ordnungsgemäß durch.

21
Vlad Mihalcea

Dies scheint in vielen Versionen von Hibernate ein Fehler zu sein. Ich habe es mit EclipseLink getestet und es funktioniert dort problemlos. 

Als Workaround in Hibernate (getestet in Hibernate 4.3.6-Final): Entfernen Sie alle Kaskadierungen in der Entität Feature und fügen Sie CascadeType.PERSIST (oder CascadeType.ALL) in der Entität Product hinzu.

Um sicherzustellen, dass es nicht funktioniert, versuchen Sie Folgendes:

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)
2
Andrei I

Ich hatte kürzlich ein ähnliches Problem. Für mich bestand das Problem darin, dass die Waisen immer noch von einer anderen verwalteten Entität referenziert wurden und für diese Beziehung eine PERSIST-Kaskadierung definiert war:

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

Angenommen, alle beteiligten Entitäten werden verwaltet, d. H. In den Persistenzkontext geladen. Jetzt machen wir dasselbe wie das OP:

// Clear and add attempt
product.getFeatures().clear();

Feature feature = new Feature(product, ls);
product.getFeatures().add(feature);

Aus philosophischer Sicht besteht das Problem darin, dass das Objektmodell inkonsistent wird, wenn Sie das Feature nur aus der Product-Entität, nicht aber aus der Description-Entität entfernen. Schließlich möchten Sie, dass das Feature entfernt wird, aber es wird immer noch von anderen Objekten referenziert. Technisch gesehen passiert es, dass zwei widersprüchliche Kaskadierungen an die Feature-Entität gehen, und das Ergebnis hängt von der Reihenfolge ab, in der sie angewendet werden.

Da das Feature im Produkt aus der Sammlung entfernt wurde, gilt die Orphan-Entfernung und das Feature-Entity wechselt während des nächsten Flushs in den Zustand "remove", wie in der JPA 2.1-Spezifikation (2.9) angegeben. Ich habe den Schwerpunkt auf die relevanten Teile gelegt:

Zuordnungen, die als OneToOne- oder OneToMany-Unterstützung angegeben sind, verwenden Der Option orphanRemoval. Die folgenden Verhaltensweisen gelten, wenn OrphanRemoval aktiviert ist:

  • Wenn eine Entität, die das Ziel der Beziehung Ist, aus der Beziehung entfernt wird (durch Festlegen der Beziehung Auf Null oder Entfernen der Entität aus der Sammlung der Sammlung ), Wird der Vorgang zum Entfernen ausgeführt wird auf die Entität angewendet, die verwaist ist. Der Vorgang zum Entfernen wird zum Zeitpunkt des Spülvorgangs Ausgeführt}. Die OrphanRemoval-Funktion ist für Entitäten Gedacht, die sich im privaten Besitz ihrer übergeordneten Entität befinden. Portable - Anwendungen dürfen ansonsten nicht von einer bestimmten Reihenfolge der Entfernung von Abhängen und darf nicht eine Entität, die verwaist wurde, einer Einer anderen Beziehung zuordnen oder Andernfalls versuchen Sie es zu behalten. Wenn die verwaiste Entität Eine abgelöste, neue oder entfernte Entität ist, wird die Semantik von OrphanRemoval nicht angewendet.

Auf dasselbe Feature wird jedoch immer noch von einer Description-Entität verwiesen, deren PERSIST auf das Feature kaskadiert ist. Die JPA 2.1-Spezifikation sagt Folgendes aus:

Die Semantik der Flush-Operation, die auf eine Entität X angewendet wird, lautet wie folgt: :

  • Wenn X eine verwaltete Entität ist, wird sie mit der Datenbank Synchronisiert.

    • Für alle Entitäten Y, auf die durch eine Beziehung von X verwiesen wird, wurde die Beziehung zu Y mit dem Kaskadenelement Wert cascade = PERSIST oder cascade = ALL kommentiert, und die persistente Operation wird angewendet. .] an Y.

Diese Kaskadierung führt also eine "persistente" Operation für die Feature-Entität durch, selbst wenn wir nicht em.persist () für die Beschreibung aufrufen. Es reicht aus, dass die Beschreibung verwaltet wird, wenn ein Flush ausgeführt wird, um diese permanente Kaskadierung auszulösen.

Dies bedeutet, dass wir genau das tun, was uns die Spezifikation gesagt hat, dass wir dies nicht tun sollten - Orphan-Entfernung durchführen und auf derselben Entität bestehen. In der Praxis scheint es in Hibernate so zu sein, dass beide Operationen nacheinander ausgeführt werden. Beim Entfernen wird zunächst die Feature-Entität in den Status "Entfernt" versetzt. Anschließend wird die entfernte Entität wieder in eine verwaltete Entität umgewandelt. Daher wird das Feature nicht aus der Datenbank entfernt.

0
Medo42

In Abschnitt 2.9, Entitätenbeziehungen, heißt es in der JPA 2.1-Spezifikation:

Wenn die verwaiste Entität eine abgelöste, neue oder entfernte Entität ist, , Gilt die Semantik von OrphanRemoval nicht.

Sind Sie sicher, dass Ihre Entität im Persistenzkontext verwaltet wird, wenn Sie sie aus der Sammlung entfernen?

Sie können das Problem beheben, indem Sie fetch=fetchType.EAGER oder fetch joins verwenden. Alternativ (es hängt von Ihrem Anwendungsfall ab) kann es ausreichend sein, die entsprechende Option cascade einzustellen.

0