Objecten verwijderen met slaapstand

1. Overzicht

Als een volledig uitgerust ORM-framework is Hibernate verantwoordelijk voor het levenscyclusbeheer van persistente objecten (entiteiten), inclusief CRUD-bewerkingen zoals lezen, sparen, bijwerken en verwijderen.

In dit artikel verkennen we verschillende manieren waarop objecten kunnen worden verwijderd uit een database met behulp van Hibernate en we leggen veelvoorkomende problemen en valkuilen uit die kunnen optreden.

We gebruiken JPA en doen alleen een stap terug en gebruiken de Hibernate native API voor die functies die niet gestandaardiseerd zijn in JPA.

2. Verschillende manieren om objecten te verwijderen

Objecten kunnen worden verwijderd in de volgende scenario's:

  • Door het gebruiken van EntityManager.verwijderen
  • Wanneer een verwijdering trapsgewijs wordt uitgevoerd vanuit andere entiteitsinstanties
  • Wanneer een weesVerwijdering is toegepast
  • Door een verwijderen JPQL-instructie
  • Door native queries uit te voeren
  • Door een zachte verwijderingstechniek toe te passen (voorlopig verwijderde entiteiten filteren op een voorwaarde in een @Waar clausule)

In de rest van het artikel kijken we in detail naar deze punten.

3. Verwijderen met behulp van Entity Manager

Verwijderen met de EntityManager is de meest eenvoudige manier om een ‚Äč‚Äčentiteitsinstantie te verwijderen:

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

In de voorbeelden in dit artikel gebruiken we een hulpmethode om de persistentiecontext te verwijderen en te wissen wanneer dat nodig is:

leegte flushAndClear () {entityManager.flush (); entiteitManager.clear (); }

Nadat u het EntityManager.verwijderen methode, gaat de opgegeven instantie over naar de verwijderd staat en de bijbehorende verwijdering uit de database vindt plaats bij de volgende spoeling.

Let daar op verwijderde instantie wordt opnieuw behouden als een DOORGAAN operatie wordt erop toegepast. Een veelgemaakte fout is om te negeren dat a DOORGAAN bewerking is toegepast op een verwijderde instantie (meestal omdat deze tijdens het spoelen vanuit een andere instantie wordt gecascadeerd), omdat de sectie 3.2.2 van de PPV-specificatie schrijft voor dat een dergelijk geval in een dergelijk geval opnieuw moet worden gehandhaafd.

We illustreren dit door een te definiëren @ManyToOne vereniging van Foo naar Bar:

@Entity openbare klasse Foo {@ManyToOne (fetch = FetchType.LAZY, cascade = CascadeType.ALL) privébalk; // andere toewijzingen, getters en setters}

Wanneer we een Bar instantie waarnaar wordt verwezen door een Foo instantie die ook wordt geladen in de persistentiecontext, de Bar instantie wordt niet verwijderd uit de database:

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

Als het verwijderd Bar wordt verwezen door een Foo, de DOORGAAN operatie is trapsgewijs van Foo naar Bar omdat de associatie is gemarkeerd met cascade = CascadeType.ALL en de verwijdering is ongepland. Om te controleren of dit gebeurt, kunnen we traceringslogboekniveau inschakelen voor het org. overwinteren pakket en zoek naar items zoals verwijdering van de entiteit ongedaan maken.

4. Trapsgewijze verwijdering

Het verwijderen kan worden overgedragen naar onderliggende entiteiten wanneer ouders worden verwijderd:

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

Hier bar wordt verwijderd omdat de verwijdering trapsgewijs plaatsvindt van foo, aangezien wordt verklaard dat de associatie alle levenscyclusbewerkingen trapsgewijs uitvoert Foo naar Bar.

Let daar op het is bijna altijd een bug om te cascaderen VERWIJDEREN operatie in een @Veel te veel vereniging, omdat dat zou leiden tot het verwijderen van onderliggende instanties die mogelijk zijn gekoppeld aan andere bovenliggende instanties. Dit geldt ook voor CascadeType.ALL, omdat het betekent dat alle bewerkingen trapsgewijs moeten worden uitgevoerd, inclusief de VERWIJDEREN operatie.

5. Verwijdering van wezen

De weesVerwijdering richtlijn verklaart dat geassocieerde entiteitsinstanties moeten worden verwijderd wanneer ze worden losgekoppeld van de bovenliggende entiteit, of equivalent wanneer de bovenliggende entiteit wordt verwijderd.

We laten dit zien door een dergelijke associatie te definiëren van Bar naar Baz:

@Entity openbare klasse Bar {@OneToMany (cascade = CascadeType.ALL, orphanRemoval = true) privélijst bazList = nieuwe ArrayList (); // andere toewijzingen, getters en setters}

Dan een Baz instantie wordt automatisch verwijderd wanneer deze uit de lijst van een ouder wordt verwijderd Bar voorbeeld:

Bar bar = nieuwe bar ("bar"); Baz baz = nieuwe Baz ("baz"); bar.getBazList (). add (baz); entiteitManager.persist (balk); 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 ());

De semantiek van de weesVerwijdering operatie is volledig vergelijkbaar met een VERWIJDEREN bewerking die rechtstreeks op de getroffen kindinstanties wordt toegepast, wat betekent dat de VERWIJDEREN bewerking wordt verder gecascadeerd naar geneste kinderen. Als gevolg hiervan moet u ervoor zorgen dat geen enkele andere instantie verwijst naar de verwijderde (anders worden ze opnieuw bewaard).

6. Verwijderen met behulp van een JPQL-instructie

Hibernate ondersteunt verwijderbewerkingen in DML-stijl:

Foo foo = nieuwe Foo ("foo"); entiteitManager.persist (foo); flushAndClear (); entiteitManager.createQuery ("verwijder uit Foo waar id =: id") .setParameter ("id", foo.getId ()) .executeUpdate (); assertThat (entityManager.find (Foo.class, foo.getId ()), nullValue ());

Het is belangrijk om in acht te nemen dat JPQL-instructies in DML-stijl zijn niet van invloed op de status of levenscyclus van entiteitsinstances die al in de persistentiecontext zijn geladen, dus het wordt aanbevolen om ze uit te voeren voordat de betrokken entiteiten worden geladen.

7. Verwijderen met behulp van native query's

Soms moeten we terugvallen op native queries om iets te bereiken dat niet wordt ondersteund door Hibernate of specifiek is voor een databaseleverancier. We kunnen ook gegevens in de database verwijderen met native queries:

Foo foo = nieuwe Foo ("foo"); entiteitManager.persist (foo); flushAndClear (); entiteitManager.createNativeQuery ("verwijderen uit FOO waarbij ID =: id") .setParameter ("id", foo.getId ()) .executeUpdate (); assertThat (entityManager.find (Foo.class, foo.getId ()), nullValue ());

Dezelfde aanbeveling is van toepassing op native queries als voor JPA DML-style statements, d.w.z. native query's hebben geen invloed op de staat of levenscyclus van entiteitsinstanties die in de persistentieverband worden geladen voordat de query's worden uitgevoerd.

8. Zachte verwijdering

Vaak is het niet wenselijk om gegevens uit een database te verwijderen vanwege controledoeleinden en het bijhouden van geschiedenis. In dergelijke situaties kunnen we een techniek toepassen die we zachte deletes noemen. In feite markeren we een rij gewoon als verwijderd en filteren we deze eruit bij het ophalen van gegevens.

Om veel overbodige omstandigheden in te voorkomen waar clausules in alle query's die tijdelijk verwijderbare entiteiten lezen, biedt Hibernate de @Waar annotatie die op een entiteit kan worden geplaatst en die een SQL-fragment bevat dat automatisch wordt toegevoegd aan SQL-query's die voor die entiteit zijn gegenereerd.

Om dit te demonstreren, voegen we de @Waar annotatie en een kolom met de naam VERWIJDERD naar de Foo entiteit:

@Entity @Where (clausule = "DELETED = 0") openbare klasse Foo {// andere toewijzingen @Column (naam = "DELETED") privé Geheel getal verwijderd = 0; // getters en setters public void setDeleted () {this.deleted = 1; }}

De volgende test bevestigt dat alles werkt zoals verwacht:

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

9. Conclusie

In dit artikel hebben we gekeken naar verschillende manieren waarop gegevens kunnen worden verwijderd met Hibernate. We hebben basisconcepten en enkele best practices uitgelegd. We hebben ook laten zien hoe soft-deletes eenvoudig kunnen worden geïmplementeerd met Hibernate.

De implementatie van deze zelfstudie Objecten verwijderen met slaapstand is beschikbaar op Github. Dit is een op Maven gebaseerd project, dus het zou gemakkelijk moeten kunnen worden geïmporteerd en uitgevoerd zoals het is.