Sletning af objekter med dvale

1. Oversigt

Som en fuldt udstyret ORM-ramme er Hibernate ansvarlig for livscyklustyring af vedvarende objekter (enheder), herunder CRUD-operationer som f.eks. Læs, Gemme, opdatering og slet.

I denne artikel undersøger vi forskellige måder, hvorpå objekter kan slettes fra en database ved hjælp af dvale og vi forklarer almindelige problemer og faldgruber, der kan opstå.

Vi bruger JPA og træder kun tilbage og bruger Hibernate native API til de funktioner, der ikke er standardiseret i JPA.

2. Forskellige måder at slette objekter på

Objekter kan slettes i følgende scenarier:

  • Ved hjælp af EntityManager.remove
  • Når en sletning kaskaderes fra andre forekomster af enheder
  • Når en fjernelse af forældreløs anvendes
  • Ved at udføre en slet JPQL-erklæring
  • Ved at udføre indfødte forespørgsler
  • Ved at anvende en blød sletningsteknik (filtrering af bløde slettede enheder efter en betingelse i en @Hvor klausul)

I den resterende del af artiklen ser vi på disse punkter i detaljer.

3. Sletning ved hjælp af Entity Manager

Sletning med EntityManager er den mest ligefremme måde at fjerne en enhedsinstans på:

Foo foo = ny Foo ("foo"); entityManager.persist (foo); flushAndClear (); foo = entityManager.find (Foo.class, foo.getId ()); hævder, at (foo, notNullValue ()); entityManager.remove (foo); flushAndClear (); assertThat (entityManager.find (Foo.class, foo.getId ()), nullValue ()); 

I eksemplerne i denne artikel bruger vi en hjælpemetode til at skylle og rydde vedholdenhedskonteksten, når det er nødvendigt:

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

Efter at have ringet til EntityManager.remove metode overgår den leverede forekomst til fjernet tilstand og den tilknyttede sletning fra databasen sker ved næste skylning.

Noter det slettet instans er vedvarende, hvis en PERSIST operation anvendes på den. En almindelig fejl er at ignorere det PERSIST operation er blevet anvendt på en fjernet forekomst (normalt fordi den kaskaderes fra en anden forekomst på skylningstidspunktet), fordi sektionen 3.2.2 af JPA-specifikationen pålægger, at en sådan instans skal fortsættes i et sådant tilfælde.

Vi illustrerer dette ved at definere a @ManyToOne forening fra Foo til Bar:

@Entity offentlig klasse Foo {@ManyToOne (fetch = FetchType.LAZY, cascade = CascadeType.ALL) privat barbar; // andre kortlægninger, getters og settere}

Når vi sletter en Bar eksempel der henvises til af en Foo forekomst, som også indlæses i vedholdenhedskonteksten, Bar forekomst fjernes ikke fra databasen:

Barbjælke = ny bjælke ("bjælke"); Foo foo = ny 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 ());

Hvis den fjernes Bar refereres til af en Foo, det PERSIST betjeningen er kaskadet fra Foo til Bar fordi foreningen er markeret med kaskade = CascadeType.ALL og sletningen er ikke planlagt. For at kontrollere, at dette sker, kan vi muligvis aktivere sporingslogniveau for org. dvale pakke og søg efter poster som f.eks fjernelse af planlægning af sletning af enhed.

4. Kaskadet sletning

Sletning kan kaskaderes til børnenheder, når forældre fjernes:

Barbjælke = ny bjælke ("bjælke"); Foo foo = ny 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 ());

Her bar fjernes, fordi fjernelse er kaskadet fra foo, da foreningen erklæres for at kaskade alle livscyklusoperationer fra Foo til Bar.

Noter det det er næsten altid en fejl at kaskade FJERNE drift i en @ManyToMany forening, fordi det ville udløse fjernelse af underforekomster, der kan være forbundet med andre forældreforekomster. Dette gælder også for CascadeType.ALL, da det betyder, at alle operationer skal kaskaderes, inklusive FJERNE operation.

5. Fjernelse af forældreløse børn

Det fjernelse af forældreløs direktivet erklærer, at tilknyttede enhedsforekomster skal fjernes, når de adskilles fra moderselskabet eller tilsvarende, når forældrene fjernes.

Vi viser dette ved at definere en sådan sammenslutning fra Bar til Baz:

@Entity public class Bar {@OneToMany (cascade = CascadeType.ALL, orphanRemoval = true) privat Liste bazList = ny ArrayList (); // andre kortlægninger, getters og settere}

Så a Baz forekomst slettes automatisk, når den fjernes fra listen over en forælder Bar eksempel:

Barbjælke = ny bjælke ("bjælke"); Baz baz = ny Baz ("baz"); bar.getBazList (). tilføj (baz); entityManager.persist (bar); flushAndClear (); bar = entityManager.find (Bar.class, bar.getId ()); baz = bar.getBazList (). get (0); bar.getBazList (). fjern (baz); flushAndClear (); assertThat (entityManager.find (Baz.class, baz.getId ()), nullValue ());

Semantikken i fjernelse af forældreløs betjening ligner fuldstændigt en FJERNE operation, der anvendes direkte på de berørte barneforekomster, hvilket betyder, at FJERNE Betjeningen kaskades yderligere til indlejrede børn. Som en konsekvens er du nødt til at sikre, at ingen andre forekomster henviser til de fjernede (ellers fortsættes de).

6. Sletning ved hjælp af en JPQL-erklæring

Dvaletilstand understøtter sletning af DML-stil:

Foo foo = ny Foo ("foo"); entityManager.persist (foo); flushAndClear (); entityManager.createQuery ("slet fra Foo hvor id =: id") .setParameter ("id", foo.getId ()). executeUpdate (); assertThat (entityManager.find (Foo.class, foo.getId ()), nullValue ());

Det er vigtigt at bemærke det JPQL-sætninger i DML-stil påvirker hverken tilstanden eller livscyklussen for enhedsforekomster, der allerede er indlæst i persistens-sammenhængen, så det anbefales, at de udføres, inden de berørte enheder indlæses.

7. Sletning ved hjælp af indfødte forespørgsler

Nogle gange er vi nødt til at falde tilbage til oprindelige forespørgsler for at opnå noget, der ikke understøttes af dvale eller er specifikt for en databaseleverandør. Vi kan også slette data i databasen med indfødte forespørgsler:

Foo foo = ny Foo ("foo"); entityManager.persist (foo); flushAndClear (); entityManager.createNativeQuery ("slet fra FOO hvor ID =: id") .setParameter ("id", foo.getId ()). executeUpdate (); assertThat (entityManager.find (Foo.class, foo.getId ()), nullValue ());

Den samme anbefaling gælder for indfødte forespørgsler som for JPA DML-stil erklæringer, dvs. indfødte forespørgsler påvirker hverken tilstanden eller livscyklussen for enhedsforekomster, der indlæses i persistens-konteksten før udførelsen af ​​forespørgslerne.

8. Blød sletning

Ofte er det ikke ønskeligt at fjerne data fra en database på grund af revisionsformål og historikopbevaring. I sådanne situationer kan vi anvende en teknik kaldet soft deletes. Dybest set markerer vi bare en række som fjernet, og vi filtrerer den ud, når vi henter data.

For at undgå mange overflødige forhold i hvor klausuler i alle forespørgsler, der læser soft-sletbare enheder, giver Hibernate den @Hvor kommentar, som kan placeres på en enhed, og som indeholder et SQL-fragment, der automatisk føjes til SQL-forespørgsler, der er genereret for den enhed.

For at demonstrere dette tilføjer vi @Hvor kommentar og en kolonne navngivet SLETTET til Foo enhed:

@Entity @Where (clause = "DELETED = 0") offentlig klasse Foo {// andre tilknytninger @Column (name = "DELETED") privat Heltal slettet = 0; // getters and setters public void setDeleted () {this.deleted = 1; }}

Følgende test bekræfter, at alt fungerer som forventet:

Foo foo = ny 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. Konklusion

I denne artikel kiggede vi på forskellige måder, hvorpå data kan slettes med dvale. Vi forklarede grundlæggende begreber og nogle bedste praksis. Vi demonstrerede også, hvordan soft-sletninger let kan implementeres med dvale.

Implementeringen af ​​denne Sletning af objekter med dvalevejledning er tilgængelig på Github. Dette er et Maven-baseret projekt, så det skal være let at importere og køre som det er.