JPA OneToMany löscht kein Kind


158

Ich habe ein Problem mit einer einfachen @OneToManyZuordnung zwischen einer übergeordneten und einer untergeordneten Entität. Alles funktioniert gut, nur dass untergeordnete Datensätze nicht gelöscht werden, wenn ich sie aus der Sammlung entferne.

Das Elternteil:

@Entity
public class Parent {
    @Id
    @Column(name = "ID")
    private Long id;

    @OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent")
    private Set<Child> childs = new HashSet<Child>();

 ...
}

Das Kind:

@Entity
public class Child {
    @Id
    @Column(name = "ID")
    private Long id;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="PARENTID", nullable = false)
    private Parent parent;

  ...
}

Wenn ich jetzt ein Kind aus dem Kinder-Set lösche, wird es nicht aus der Datenbank gelöscht. Ich habe versucht, die child.parentReferenz aufzuheben , aber das hat auch nicht funktioniert.

Die Entitäten werden in einer Webanwendung verwendet, das Löschen erfolgt im Rahmen einer Ajax-Anfrage. Ich habe keine Liste der gelöschten Kinder, wenn die Schaltfläche Speichern gedrückt wird, daher kann ich sie nicht implizit löschen.

Antworten:


252

Das Verhalten von JPA ist korrekt (dh gemäß Spezifikation ): Objekte werden nicht gelöscht, nur weil Sie sie aus einer OneToMany-Sammlung entfernt haben. Es gibt herstellerspezifische Erweiterungen, die dies tun, aber native JPA berücksichtigt dies nicht.

Dies liegt zum Teil daran, dass JPA nicht weiß, ob etwas aus der Sammlung entfernt werden soll. In Bezug auf die Objektmodellierung ist dies der Unterschied zwischen Zusammensetzung und "Aggregation *".

In der Zusammensetzung existiert die untergeordnete Entität ohne die übergeordnete Entität nicht. Ein klassisches Beispiel ist zwischen Haus und Raum. Löschen Sie das Haus und die Zimmer gehen auch.

Aggregation ist eine lockere Art von Assoziation und wird durch Kurs und Student charakterisiert. Löschen Sie den Kurs und der Student existiert noch (wahrscheinlich in anderen Kursen).

Sie müssen also entweder herstellerspezifische Erweiterungen verwenden, um dieses Verhalten zu erzwingen (falls verfügbar) oder das untergeordnete Element explizit löschen UND aus der übergeordneten Sammlung entfernen.

Ich bin mir bewusst über:


danke die nette erklärung. so ist es, wie ich befürchtet habe. (Ich habe etwas gesucht / gelesen, bevor ich gefragt habe, nur um sicher zu sein). Irgendwie bereue ich die Entscheidung, JPA API und nit Hibernate direkt zu verwenden. Ich werde den Chandra Patni-Zeiger ausprobieren und den Kaskadentyp delete_orphan im Ruhezustand verwenden.
Bert

Ich habe eine ähnliche Frage dazu. Bitte schauen Sie sich diesen Beitrag hier an. stackoverflow.com/questions/4569857/…
Thang Pham

77
Mit JPA 2.0 können Sie jetzt die Option orphanRemoval = true
itsadok

2
Tolle erste Erklärung und gute Ratschläge zur OrphanRemoval. Hatte keine Ahnung, dass JPA diese Art der Entfernung nicht berücksichtigt. Die Nuancen zwischen dem, was ich über Hibernate weiß und dem, was JPA tatsächlich tut, können verrückt werden.
Sma

Schön erklärt, indem man die Unterschiede zwischen Zusammensetzung und Aggregation aufdeckt!
Felipe Leão

73

Zusätzlich zur Antwort von cletus führt JPA 2.0 , das seit Dezember 2010 endgültig ist, ein orphanRemovalAttribut für @OneToManyAnmerkungen ein. Weitere Details finden Sie in diesem Blogeintrag .

Da die Spezifikation relativ neu ist, verfügen nicht alle JPA 1-Anbieter über eine endgültige JPA 2-Implementierung. Beispielsweise unterstützt die Version Hibernate 3.5.0-Beta-2 dieses Attribut noch nicht.


Blogeintrag - Link ist defekt.
Steffi

1
Und die Magie der Wayback-Maschine rettet uns: web.archive.org/web/20120225040254/http://javablog.co.uk/2009/…
Louis Jacomet

43

Sie können dies versuchen:

@OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true).


2
Vielen Dank. Aber die Frage ist 1 Mal von der JPA zurück. Und diese Option war damals nicht verfügbar.
Bert

9
Gut zu wissen, für diejenigen von uns, die in der JPA2-Zeit nach einer Lösung dafür suchen :)
alex440

20

Wie bereits erläutert, ist es mit JPA nicht möglich, das zu tun, was ich möchte. Daher habe ich die Annotation hibernate.cascade verwendet. Damit sieht der relevante Code in der Parent-Klasse jetzt folgendermaßen aus:

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

Ich konnte nicht einfach 'ALL' verwenden, da dies auch den Elternteil gelöscht hätte.


4

Hier bedeutet Kaskade im Zusammenhang mit Entfernen, dass die untergeordneten Elemente entfernt werden, wenn Sie das übergeordnete Element entfernen. Nicht der Verein. Wenn Sie Hibernate als Ihren JPA-Anbieter verwenden, können Sie dies mithilfe einer spezifischen Ruhekaskade tun .


4

Sie können dies versuchen:

@OneToOne(cascade = CascadeType.REFRESH) 

oder

@OneToMany(cascade = CascadeType.REFRESH)

3
@Entity 
class Employee {
     @OneToOne(orphanRemoval=true)
     private Address address;
}

Siehe hier .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.