Objekte im Ruhezustand löschen

1. Übersicht

Als voll funktionsfähiges ORM-Framework ist Hibernate für das Lebenszyklusmanagement persistenter Objekte (Entitäten) verantwortlich, einschließlich CRUD-Vorgängen wie Lesen , Speichern , Aktualisieren und Löschen .

In diesem Artikel untersuchen wir verschiedene Möglichkeiten, wie Objekte mithilfe des Ruhezustands aus einer Datenbank gelöscht werden können, und erläutern häufig auftretende Probleme und Fallstricke.

Wir verwenden JPA und treten nur einen Schritt zurück und verwenden die native Hibernate-API für Funktionen, die in JPA nicht standardisiert sind.

2. Verschiedene Möglichkeiten zum Löschen von Objekten

Objekte können in den folgenden Szenarien gelöscht werden:

  • Mit EntityManager.remove
  • Wenn eine Löschung von anderen Entitätsinstanzen kaskadiert wird
  • Wenn eine OrphanRemoval angewendet wird
  • Durch eine Ausführung löschen JPQL Anweisung
  • Durch Ausführen nativer Abfragen
  • Durch Anwenden einer Soft-Deletion-Technik (Filtern von Soft-gelöschten Entitäten nach einer Bedingung in einer @ Where- Klausel)

Im Rest des Artikels werden diese Punkte im Detail betrachtet.

3. Löschen mit dem Entity Manager

Das Löschen mit dem EntityManager ist der einfachste Weg, eine Entitätsinstanz zu entfernen:

Foo foo = new Foo("foo"); entityManager.persist(foo); flushAndClear(); foo = entityManager.find(Foo.class, foo.getId()); assertThat(foo, notNullValue()); entityManager.remove(foo); flushAndClear(); assertThat(entityManager.find(Foo.class, foo.getId()), nullValue()); 

In den Beispielen in diesem Artikel verwenden wir eine Hilfsmethode, um den Persistenzkontext bei Bedarf zu leeren und zu löschen:

void flushAndClear() { entityManager.flush(); entityManager.clear(); }

Nach dem Aufrufen der EntityManager.remove- Methode wechselt die angegebene Instanz in den entfernten Status und das zugehörige Löschen aus der Datenbank erfolgt beim nächsten Flush.

Beachten Sie, dass die gelöschte Instanz erneut beibehalten wird, wenn eine PERSIST- Operation darauf angewendet wird . Ein häufiger Fehler besteht darin, zu ignorieren, dass eine PERSIST- Operation auf eine entfernte Instanz angewendet wurde (normalerweise, weil sie zum Flush-Zeitpunkt von einer anderen Instanz kaskadiert wird), da in Abschnitt 3.2.2 der JPA-Spezifikation vorgeschrieben ist, dass eine solche Instanz sein soll bestand in einem solchen Fall wieder.

Wir veranschaulichen dies, indem wir eine @ VieleToOne- Zuordnung von Foo zu Bar definieren :

@Entity public class Foo { @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) private Bar bar; // other mappings, getters and setters }

Wenn wir eine Bar- Instanz löschen, auf die von einer Foo- Instanz verwiesen wird, die auch im Persistenzkontext geladen ist, wird die Bar- Instanz nicht aus der Datenbank entfernt:

Bar bar = new Bar("bar"); Foo foo = new Foo("foo"); foo.setBar(bar); entityManager.persist(foo); flushAndClear(); foo = entityManager.find(Foo.class, foo.getId()); bar = entityManager.find(Bar.class, bar.getId()); entityManager.remove(bar); flushAndClear(); bar = entityManager.find(Bar.class, bar.getId()); assertThat(bar, notNullValue()); foo = entityManager.find(Foo.class, foo.getId()); foo.setBar(null); entityManager.remove(bar); flushAndClear(); assertThat(entityManager.find(Bar.class, bar.getId()), nullValue());

Wenn die entfernte Bar durch eine referenziert wird Foo , die PERSIST Betrieb von kaskadierten ist Foo zu Bar , weil der Verein mit markiert Kaskade = CascadeType.ALL und die Löschung ohne Zeitplan wird. Um zu überprüfen, ob dies geschieht, aktivieren wir möglicherweise die Ablaufverfolgungsprotokollebene für das Paket org.hibernate und suchen nach Einträgen, z. B. dem Löschen von Entitäten ohne Zeitplanung .

4. Kaskadiertes Löschen

Das Löschen kann an untergeordnete Entitäten kaskadiert werden, wenn Eltern entfernt werden:

Bar bar = new Bar("bar"); Foo foo = new Foo("foo"); foo.setBar(bar); entityManager.persist(foo); flushAndClear(); foo = entityManager.find(Foo.class, foo.getId()); entityManager.remove(foo); flushAndClear(); assertThat(entityManager.find(Foo.class, foo.getId()), nullValue()); assertThat(entityManager.find(Bar.class, bar.getId()), nullValue());

Hier bar wird entfernt , da die Entfernung von kaskadiert wird foo , da der Verband deklariert alle Lebenszyklus - Operationen von kaskadiert Foo zu Bar .

Beachten Sie, dass es fast immer ein Fehler ist, die REMOVE- Operation in einer @ ManyToMany- Zuordnung zu kaskadieren , da dies das Entfernen von untergeordneten Instanzen auslösen würde, die möglicherweise anderen übergeordneten Instanzen zugeordnet sind. Dies gilt auch für CascadeType.ALL , da alle Vorgänge einschließlich der Operation REMOVE kaskadiert werden müssen .

5. Entfernung von Waisenkindern

Die orphanRemoval- Direktive erklärt, dass zugeordnete Entitätsinstanzen entfernt werden sollen, wenn sie vom übergeordneten Element getrennt werden, oder gleichwertig, wenn das übergeordnete Element entfernt wird.

Wir zeigen dies, indem wir eine solche Assoziation von Bar zu Baz definieren:

@Entity public class Bar { @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) private List bazList = new ArrayList(); // other mappings, getters and setters }

Dann wird eine Baz- Instanz automatisch gelöscht, wenn sie aus der Liste einer übergeordneten Bar- Instanz entfernt wird:

Bar bar = new Bar("bar"); Baz baz = new Baz("baz"); bar.getBazList().add(baz); entityManager.persist(bar); flushAndClear(); bar = entityManager.find(Bar.class, bar.getId()); baz = bar.getBazList().get(0); bar.getBazList().remove(baz); flushAndClear(); assertThat(entityManager.find(Baz.class, baz.getId()), nullValue());

Die Semantik der orphanRemoval- Operation ähnelt vollständig einer REMOVE- Operation, die direkt auf die betroffenen untergeordneten Instanzen angewendet wird. Dies bedeutet, dass die REMOVE- Operation weiter auf verschachtelte untergeordnete Instanzen kaskadiert wird. Infolgedessen müssen Sie sicherstellen, dass keine anderen Instanzen auf die entfernten verweisen (andernfalls bleiben sie bestehen).

6. Löschen mit einer JPQL-Anweisung

Der Ruhezustand unterstützt Löschvorgänge im DML-Stil:

Foo foo = new Foo("foo"); entityManager.persist(foo); flushAndClear(); entityManager.createQuery("delete from Foo where id = :id") .setParameter("id", foo.getId()) .executeUpdate(); assertThat(entityManager.find(Foo.class, foo.getId()), nullValue());

Es ist wichtig zu beachten, dass JPQL-Anweisungen im DML-Stil weder den Status noch den Lebenszyklus von Entitätsinstanzen beeinflussen, die bereits in den Persistenzkontext geladen wurden. Daher wird empfohlen, sie vor dem Laden der betroffenen Entitäten auszuführen.

7. Löschen mit nativen Abfragen

Sometimes we need to fall back to native queries to achieve something that is not supported by Hibernate or is specific to a database vendor. We may also delete data in the database with native queries:

Foo foo = new Foo("foo"); entityManager.persist(foo); flushAndClear(); entityManager.createNativeQuery("delete from FOO where ID = :id") .setParameter("id", foo.getId()) .executeUpdate(); assertThat(entityManager.find(Foo.class, foo.getId()), nullValue());

The same recommendation applies to native queries as for JPA DML-style statements, i.e. native queries affect neither the state nor life cycle of entity instances which are loaded into the persistence context prior to execution of the queries.

8. Soft Deletion

Often it is not desirable to remove data from a database because of auditing purposes and keeping history. In such situations, we may apply a technique called soft deletes. Basically, we just mark a row as removed and we filter it out when retrieving data.

In order to avoid lots of redundant conditions in where clauses in all the queries that read soft-deletable entities, Hibernate provides the @Where annotation which can be placed on an entity and which contains an SQL fragment that is automatically added to SQL queries generated for that entity.

To demonstrate this, we add the @Where annotation and a column named DELETED to the Foo entity:

@Entity @Where(clause = "DELETED = 0") public class Foo { // other mappings @Column(name = "DELETED") private Integer deleted = 0; // getters and setters public void setDeleted() { this.deleted = 1; } }

The following test confirms that everything works as expected:

Foo foo = new Foo("foo"); entityManager.persist(foo); flushAndClear(); foo = entityManager.find(Foo.class, foo.getId()); foo.setDeleted(); flushAndClear(); assertThat(entityManager.find(Foo.class, foo.getId()), nullValue());

9. Conclusion

In this article, we looked at different ways in which data can be deleted with Hibernate. We explained basic concepts and some best practices. We also demonstrated how soft-deletes can be easily implemented with Hibernate.

Die Implementierung dieses Lernprogramms zum Löschen von Objekten mit Ruhezustand ist über Github verfügbar. Dies ist ein Maven-basiertes Projekt, daher sollte es einfach zu importieren und so wie es ist ausgeführt werden.