Brisanje objekata pomoću hibernacije

1. Pregled

Kao cjeloviti ORM okvir, Hibernate je odgovoran za upravljanje životnim ciklusom trajnih objekata (entiteta), uključujući CRUD operacije poput čitati, uštedjeti, ažuriranje i izbrisati.

U ovom članku istražujemo razne načine na koje se objekti mogu brisati iz baze podataka pomoću hibernacije i objašnjavamo uobičajene probleme i zamke koji se mogu dogoditi.

Koristimo JPA i samo se povlačimo unatrag i koristimo Hibernate native API za one značajke koje nisu standardizirane u JPA.

2. Različiti načini brisanja predmeta

Objekti se mogu brisati u sljedećim scenarijima:

  • Pomoću EntityManager.remove
  • Kada je brisanje kaskadno s drugih instanci entiteta
  • Kad an siročeUklanjanje je primijenjen
  • Izvršenjem a izbrisati Izjava JPQL
  • Izvršavanjem izvornih upita
  • Primjenom tehnike mekog brisanja (filtriranje meko izbrisanih entiteta prema stanju u @Gdje klauzula)

U ostatku članka detaljno ćemo razmotriti ove točke.

3. Brisanje pomoću upravitelja entiteta

Brisanje s EntityManager je najjednostavniji način uklanjanja instance entiteta:

Foo foo = novi 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 ()); 

U primjerima u ovom članku koristimo pomoćnu metodu za uklanjanje i čišćenje konteksta postojanosti kad je to potrebno:

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

Nakon poziva na EntityManager.remove metoda, isporučena instanca prelazi na uklonjen stanje i pridruženo brisanje iz baze podataka događa se na sljedećem ispiranju.

Imajte na umu da izbrisana instanca nastavlja se ako a USTRAJATI na njega se primjenjuje operacija. Uobičajena pogreška je zanemarivanje da a USTRAJATI operacija primijenjena je na uklonjenu instancu (obično zato što je kaskadna s druge instance u vrijeme ispiranja), jer je odjeljak 3.2.2 JPA specifikacije nalaže da se takva instanca ponovno nastavi u takvom slučaju.

To ilustriramo definiranjem a @ManyToOne udruga iz Foo do Bar:

@Entity javna klasa Foo {@ManyToOne (fetch = FetchType.LAZY, cascade = CascadeType.ALL) privatna traka Bar; // ostala mapiranja, dobivači i postavljači}

Kad izbrišemo a Bar instanca na koju upućuje a Foo instanca koja se također učitava u kontekstu postojanosti, Bar instanca neće biti uklonjena iz baze podataka:

Šipka šipke = nova šipka ("šipka"); Foo foo = novi Foo ("foo"); foo.setBar (traka); 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 ());

Ako je uklonjeno Bar na koju upućuje a Foo, USTRAJATI operacija je kaskadna od Foo do Bar jer je udruga označena s kaskada = CascadeType.ALL a brisanje je neplanirano. Da bismo potvrdili da se to događa, možemo omogućiti razinu dnevnika praćenja za org. hibernirati paket i potražite unose poput brisanje entiteta bez rasporeda.

4. Kaskadno brisanje

Brisanje se može kaskadno podijeliti na dječje entitete kada se roditelji uklone:

Šipka šipke = nova šipka ("šipka"); Foo foo = novi Foo ("foo"); foo.setBar (traka); 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 ());

Ovdje bar uklanja se jer je uklanjanje kaskadno sa foo, jer je udruženje proglašeno kaskadnim svim operacijama životnog ciklusa od Foo do Bar.

Imajte na umu da gotovo je uvijek greška u kaskadiranju UKLONITI operacija u a @ManyToMany udruživanje, jer bi to pokrenulo uklanjanje podređenih primjeraka koji mogu biti povezani s drugim roditeljskim primjerima. To se također odnosi na CascadeType.ALL, jer to znači da sve operacije trebaju biti kaskadne, uključujući i UKLONITI operacija.

5. Uklanjanje siročadi

The siročeUklanjanje direktivom izjavljuje da se udruženi primjeri entiteta trebaju ukloniti kada se odvoje od roditelja ili ekvivalentno kada se ukloni roditelj.

To pokazujemo definiranjem takve udruge iz Bar do Baz:

Traka javne klase @Entity {@OneToMany (cascade = CascadeType.ALL, orphanRemoval = true) privatni popis bazList = new ArrayList (); // ostala mapiranja, dobivači i postavljači}

Tada a Baz instanca se automatski briše kad se ukloni s popisa roditelja Bar primjer:

Šipka šipke = nova šipka ("šipka"); Baz baz = novi Baz ("baz"); bar.getBazList (). add (baz); entityManager.persist (traka); 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 ());

Semantika siročeUklanjanje operacija je potpuno slična a UKLONITI operacija primijenjena izravno na pogođene dječje slučajeve, što znači da UKLONITI operacija se dalje kaskadira za ugniježđenu djecu. Kao posljedicu toga, morate osigurati da nijedna druga instanca ne uputi na uklonjene (u suprotnom će se nastaviti).

6. Brisanje pomoću JPQL izjave

Hibernate podržava operacije brisanja u DML stilu:

Foo foo = novi Foo ("foo"); entityManager.persist (foo); flushAndClear (); entityManager.createQuery ("izbriši iz Foo-a gdje je id =: id") .setParameter ("id", foo.getId ()) .executeUpdate (); assertThat (entityManager.find (Foo.class, foo.getId ()), nullValue ());

Važno je napomenuti da JPQL izrazi u DML stilu ne utječu ni na stanje ni na životni ciklus instanci entiteta koji su već učitani u kontekst postojanosti, pa se preporučuje da se izvrše prije učitavanja zahvaćenih entiteta.

7. Brisanje pomoću izvornih upita

Ponekad se moramo vratiti na izvorne upite kako bismo postigli nešto što Hibernate ne podržava ili je specifično za dobavljača baze podataka. Također možemo izbrisati podatke iz baze podataka s izvornim upitima:

Foo foo = novi Foo ("foo"); entityManager.persist (foo); flushAndClear (); entityManager.createNativeQuery ("izbriši iz FOO-a gdje je ID =: id") .setParameter ("id", foo.getId ()) .executeUpdate (); assertThat (entityManager.find (Foo.class, foo.getId ()), nullValue ());

Ista preporuka odnosi se na nativne upite kao i za izjave u stilu JPA DML, tj. nativni upiti ne utječu ni na stanje ni na životni ciklus instanci entiteta koji su učitani u kontekst postojanosti prije izvođenja upita.

8. Meko brisanje

Često nije poželjno uklanjanje podataka iz baze podataka zbog revizije i vođenja povijesti. U takvim situacijama možemo primijeniti tehniku ​​koja se naziva meko brisanje. U osnovi, samo označavamo redak kao uklonjen i filtriramo ga prilikom dohvaćanja podataka.

Da bi se izbjeglo puno suvišnih uvjeta u sustavu Windows gdje klauzule u svim upitima koji čitaju entitete koji se mogu brisati, Hibernate pruža @Gdje napomena koja se može staviti na entitet i koja sadrži SQL fragment koji se automatski dodaje SQL upitima generiranim za taj entitet.

Da bismo to demonstrirali, dodajemo @Gdje napomena i stupac s imenom BRISANO prema Foo entitet:

@Entity @Where (klauzula = "DELETED = 0") javna klasa Foo {// ostala preslikavanja @Column (name = "DELETED") private Integer izbrisana = 0; // getteri i postavljači public void setDeleted () {this.deleted = 1; }}

Sljedeći test potvrđuje da sve funkcionira prema očekivanjima:

Foo foo = novi 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. Zaključak

U ovom smo članku pogledali različite načine na koje se podaci mogu brisati pomoću hibernacije. Objasnili smo osnovne pojmove i neke najbolje prakse. Također smo pokazali kako se softverska brisanja mogu lako implementirati s hibernacijom.

Provedba ovog vodiča za Brisanje objekata pomoću hibernacije dostupna je na Githubu. Ovo je projekt zasnovan na Mavenu, pa bi ga trebalo lako uvesti i pokrenuti takav kakav jest.