Objekte mit Ruhezustand löschen

Objekte im Ruhezustand löschen

1. Überblick

Als voll funktionsfähiges ORM-Framework ist Hibernate für das Lebenszyklusmanagement persistenter Objekte (Entitäten) verantwortlich, einschließlich CRUD-Operationen wieread,save,update unddelete.

In diesem Artikel untersuchen wir verschiedeneways in which objects may be deleted from a database using Hibernate und erläutern häufig auftretende Probleme und Fallstricke.

Wir verwenden JPA und verwenden die native Hibernate-API nur für die 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:

  • MitEntityManager.remove

  • Wenn eine Löschung aus anderen Entitätsinstanzen kaskadiert wird

  • Wenn einorphanRemoval angewendet wird

  • Durch Ausführen einerdelete JPQL-Anweisung

  • Indem Sie native Abfragen ausführen

  • Durch Anwenden einer Soft-Deletion-Technik (Filtern von Soft-gelöschten Entitäten nach einer Bedingung in einer@Where-Klausel)

Im weiteren Verlauf des Artikels werden diese Punkte im Detail betrachtet.

3. Löschen mit dem Entity Manager

Das Löschen mitEntityManager 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 Aufruf der MethodeEntityManager.remove wechselt die angegebene Instanz in den Statusremoved, und das zugehörige Löschen aus der Datenbank erfolgt beim nächsten Flush.

Beachten Sie, dassdeleted instance is re-persisted if a PERSIST operation is applied to it. Ein häufiger Fehler besteht darin, zu ignorieren, dass einePERSIST-Operation auf eine entfernte Instanz angewendet wurde (normalerweise, weil sie zum Flush-Zeitpunkt von einer anderen Instanz kaskadiert wird), da der Abschnitt3.2.2 vonJPA specificationist ) schreibt vor, dass ein solcher Fall in einem solchen Fall erneut fortgeführt werden soll.

Wir veranschaulichen dies, indem wir eine@ManyToOne-Assoziation vonFoo zuBar definieren:

@Entity
public class Foo {
    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private Bar bar;

    // other mappings, getters and setters
}

Wenn wir eineBar-Instanz löschen, auf die von einerFoo-Instanz verwiesen wird, die auch im Persistenzkontext geladen wird, wird dieBar-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 das entfernteBar durch einFoo referenziert wird, wird die OperationPERSIST vonFoo aufBar kaskadiert, da die Zuordnung mitcascade = CascadeType.ALL markiert ist und das Löschen ist außerplanmäßig. Um dies zu überprüfen, aktivieren wir möglicherweise die Ablaufverfolgungsprotokollstufe für das Paketorg.hibernateund suchen nach Einträgen wieun-scheduling entity deletion.

4. Kaskadiertes Löschen

Das Löschen kann auf untergeordnete Objekte übertragen werden, wenn übergeordnete Objekte 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 wirdbar entfernt, weil das Entfernen vonfoo kaskadiert wird, da die Zuordnung deklariert wird, alle Lebenszyklusoperationen vonFoo bisBar zu kaskadieren.

Beachten Sie, dassit is almost always a bug to cascade REMOVE operation in a @ManyToMany association ist, da dies das Entfernen von untergeordneten Instanzen auslösen würde, die möglicherweise anderen übergeordneten Instanzen zugeordnet sind. Dies gilt auch fürCascadeType.ALL, da alle Operationen kaskadiert werden sollen, einschließlich der OperationREMOVE.

5. Entfernung von Waisenkindern

Die AnweisungorphanRemoval deklariert, 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 vonBar bisBaz: definieren

@Entity
public class Bar {
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List bazList = new ArrayList<>();

    // other mappings, getters and setters
}

Dann wird die Instanz vonBazautomatisch gelöscht, wenn sie aus der Liste der Instanz vonBareines übergeordneten Elements 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());

The semantics of the orphanRemoval operation is completely similar to a REMOVE operation applied directly to the affected child instances, was bedeutet, dass die OperationREMOVE weiter auf verschachtelte Kinder 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

Hibernate 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, dassDML-style JPQL statements affect neither the state nor life cycle of entity instances that are already loaded into the persistence context ist. Daher wird empfohlen, diese vor dem Laden der betroffenen Entitäten auszuführen.

7. Löschen mit nativen Abfragen

Manchmal müssen wir auf systemeigene Abfragen zurückgreifen, um etwas zu erreichen, das von Hibernate nicht unterstützt wird oder für einen Datenbankanbieter spezifisch ist. Wir können auch Daten in der Datenbank mit systemeigenen Abfragen löschen:

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

Für native Abfragen gilt die gleiche Empfehlung wie für Anweisungen im JPA-DML-Stil, d. H. 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

Häufig ist es nicht wünschenswert, Daten aus einer Datenbank zu entfernen, da dies zu Prüfzwecken und zur Protokollierung erforderlich ist. In solchen Situationen wenden wir möglicherweise eine Technik an, die als weiches Löschen bezeichnet wird. Grundsätzlich markieren wir eine Zeile nur als entfernt und filtern sie beim Abrufen von Daten heraus.

Um viele redundante Bedingungen inwhere-Klauseln in allen Abfragen zu vermeiden, die weich löschbare Entitäten lesen, stellt Hibernate die Annotation@Where bereit, die auf einer Entität platziert werden kann und ein SQL-Fragment enthält Wird automatisch zu SQL-Abfragen hinzugefügt, die für diese Entität generiert wurden.

Um dies zu demonstrieren, fügen wir der EntitätFoodie Annotation@Where und eine Spalte mit dem NamenDELETED hinzu:

@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;
    }
}

Der folgende Test bestätigt, dass alles wie erwartet funktioniert:

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. Fazit

In diesem Artikel haben wir uns verschiedene Möglichkeiten angesehen, wie Daten mit Hibernate gelöscht werden können. Wir haben grundlegende Konzepte und einige Best Practices erläutert. Wir haben auch gezeigt, wie Soft-Deletes mit Hibernate einfach implementiert werden können.

Die Implementierung dieses Lernprogramms zum Löschen von Objekten im Ruhezustand istover on Github verfügbar. Dies ist ein Maven-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.