Seiten

Freitag, 7. Juni 2013

Löschen von JPA-Entitäten (Blättern) in Baumstrukturen (und kleine, fiese Hibernate-Bugs...)



Ich weiß nicht ob es nur mir so geht, aber im Umgang mit JPA/Hibernate tat ich mich anfangs recht schwer. Das fing schon bei trivialen Sachen (sollte man meinen) wie dem Löschen von Entitäten an.. Wenn dann noch Baumstrukturen hinzukommen, tappt man schnell in die ein oder andere Falle. Hier mal ein Beispiel einer Umsetzung einer Entitäts-Baumstruktur (inkl. dem Löschen von Blättern im Baum).

Ich habe heute eine Entität "SearchPattern" erweitert. Zuvor stand diese für sich alleine, hatte also keine Verbindungen zu anderen Tabellen / Entitäten.
Hier stellte das Löschen natürlich noch kein Problem dar:

entityManager.remove(toDeleteSearchPattern);
entityManager.flush();


bumms..fertig.

Nun habe ich die Entität dahingehend erweitert, dass sie eine Baumstruktur darstellt.
Sprich: Ein SearchPattern kann ein Parent-SearchPattern und eine Liste von Kind-SearchPattern haben. Dies realisierte ich grob skizziert folgendermaßen:

@Entity
public class SearchPattern{

    @OneToOne
    private SearchPattern parent;

    @OneToMany
    private List<SearchPattern> children;
}


Lassen wir den Fall, dass ein Parent-SearchPattern (also eine Entität die Kinder hat) gelöscht werden soll , mal außen vor und betrachten den Fall, dass eine "Blatt"-Entität gelöscht werden soll. Also ein SearchPattern, dass keine Kinder aber einen Vater hat. In diesem Fall muss der Löschvorgang innerhalb einer Transaktion nun grob so aussehen:

parentSearchPattern.getChildren().remove(toDeleteSearchPattern);
entityManager.remove(toDeleteSearchPattern);
entityManager.flush(); 


Es wird also zunächst das zu löschende SearchPattern aus der Kindliste des Vaters entfernt. Anschließend kann dann das eigentliche SearchPattern entfernt werden, da dieses nun nicht mehr in anderen Entitäten referenziert wird. Anschließend können die Änderungen geflusht werden.

Übrigens: Wer in Hibernate-Entitäten mit Collections arbeitet, sollte zumindest meiner Einschätzung nach wenn möglich auf Listen (statt Sets) setzen. Zunächst hatte ich für die Kinder ein Set benutzt. Ich habe es jedoch nicht hinbekommen, das zu löschende SearchPattern aus der Kindliste des Vaters zu entfernen. Ich dachte zunächst ich hätte einfach Tomaten auf den Augen, aber nein..alles schien okay zu sein. Es wollte mir einfach nicht in den Kopf warum ich das gewünschte SearchPattern nicht aus dem Set removen konnte. Die equals- und hashcode-Methoden der Entität waren korrekt und die SearchPattern-Instanz die ich der .remove-Methode übergeben habe war definitiv in dem Set enthalten. Woran lags? Nun, offensichtlich hat Hibernate einen Bug (und das scheinar schon recht lange! http://yigitdarcin.com/2010/01/08/hibernate-persistentset-remove-not-working/ leider funktioniert der dort verlinkte Link zum entsprechenden Bugtracking-System bei mir zur Zeit nicht) , der es manchmal unmöglich macht etwas aus dem Set einer Entität zu löschen. Nachdem ich das Set in eine List umwandelte, funktionierte das Entfernen einwandfrei.