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.

bert
quelle

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:

Cletus
quelle
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.

Louis Jacomet
quelle
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).

Maciel Bombonato
quelle
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.

bert
quelle
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 .

Chandra Patni
quelle
4

Sie können dies versuchen:

@OneToOne(cascade = CascadeType.REFRESH) 

oder

@OneToMany(cascade = CascadeType.REFRESH)
Amit Ghorpade
quelle
3
@Entity 
class Employee {
     @OneToOne(orphanRemoval=true)
     private Address address;
}

Siehe hier .

Chang
quelle