Oversigt over JPA / dvale-kaskadetyper

1. Introduktion

I denne vejledning diskuterer vi, hvad cascading er i JPA / Hibernate. Derefter dækker vi de forskellige kaskadetyper, der er tilgængelige, sammen med deres semantik.

2. Hvad er Cascading?

Enhedsforhold afhænger ofte af eksistensen af ​​en anden enhed - for eksempel PersonAdresse forhold. Uden Person, det Adresse enhed har ingen egen betydning. Når vi sletter Person enhed, vores Adresse enhed skal også slettes.

Cascading er måden at opnå dette på. Når vi udfører en handling på målenheden, vil den samme handling blive anvendt på den tilknyttede enhed.

2.1. JPA kaskadetype

Alle JPA-specifikke kaskadeoperationer er repræsenteret af javax.persistence.CascadeType enum indeholdende poster:

  • ALLE
  • PERSIST
  • FUSIONERE
  • FJERNE
  • OPDATER
  • LØSRIVE

2.2. Dvaletilstand Kaskadetype

Dvaletilstand understøtter tre yderligere kaskadetyper sammen med dem, der er specificeret af JPA. Disse dvale-specifikke kaskadetyper er tilgængelige i org.hibernate.annotations.CascadeType:

  • REPLIKAT
  • SAVE_UPDATE
  • LÅSE

3. Forskel mellem kaskadetyper

3.1. CascadeType.ALLE

Cascade.ALLforplanter alle operationer - inklusive dvale-specifikke - fra en forælder til en underordnet enhed.

Lad os se det i et eksempel:

@Entity public class Person {@Id @GeneratedValue (strategi = GenerationType.AUTO) privat int id; privat strengnavn; @OneToMany (mappedBy = "person", cascade = CascadeType.ALL) private Listeadresser; }

Bemærk, at i OneToMany foreninger, vi har nævnt kaskadetype i kommentaren.

Lad os nu se den tilknyttede enhed Adresse:

@Entity offentlig klasse Adresse {@Id @GeneratedValue (strategi = GenerationType.AUTO) privat int id; privat String street; privat int husNummer; private String by; privat int zipCode; @ManyToOne (fetch = FetchType.LAZY) privat person person; }

3.2. CascadeType.PERSIST

Den vedvarende operation gør en forbigående forekomst vedholdende. CascadeType PERSIST formerer den vedvarende operation fra en forælder til en underordnet enhed. Når vi gemmer person enhed, den adresse enhed bliver også gemt.

Lad os se test case for en vedvarende operation:

@Test offentlig ugyldig nårParentSavedThenChildSaved () {Person person = ny person (); Adresse adresse = ny adresse (); address.setPerson (person); person.setAddresses (Arrays.asList (adresse)); session.persist (person); session.flush (); session.clear (); }

Når vi kører ovenstående testtilfælde, ser vi følgende SQL:

Dvaletilstand: indsæt i personværdier (navn, id) (?,?) Dvaletilstand: indsæt i adresse (by, husnummer, person_id, gade, zipCode, id) værdier (?,?,?,?,?,?)

3.3. CascadeType.FUSIONERE

Fusionsoperationen kopierer tilstanden for det givne objekt til det vedvarende objekt med samme identifikator. CascadeType.MERGE udbreder fletteoperationen fra en forælder til en underordnet enhed.

Lad os teste fletteoperationen:

@Test offentlig ugyldig nårParentSavedThenMerged () {int addressId; Person person = buildPerson ("devender"); Adresse-adresse = buildAddress (person); person.setAddresses (Arrays.asList (adresse)); session.persist (person); session.flush (); addressId = address.getId (); session.clear (); Adresse gemtAddressEntity = session.find (Address.class, addressId); Person savedPersonEntity = gemtAdresseEntity.getPerson (); savedPersonEntity.setName ("devender kumar"); savedAddressEntity.setHouseNumber (24); session.merge (savedPersonEntity); session.flush (); }

Når vi kører ovenstående testtilfælde, genererer fletteoperationen følgende SQL:

Dvaletilstand: vælg adresse0_.id som id1_0_0_, adresse0_.by som by2_0_0_, adresse0_.husNummer som husNum3_0_0_, adresse0_.person_id som person_i6_0_0_, adresse0_.gade som gade4_0_0_, adresse0_.zipKode som zip-kode_0 Dvale: vælg person0_.id som id1_1_0_, person0_.navn som navn2_1_0_ fra Person person0_ hvor person0_.id =? Dvaletilstand: opdater Adressesæt by = ?, husnummer = ?, person_id = ?, gade = ?, zipCode =? hvor id =? Dvaletilstand: Opdater personens navn =? hvor id =?

Her kan vi se, at fusionsoperationen først indlæser begge dele adresse og person enheder og opdaterer derefter begge som et resultat af CascadeType MERGE.

3.4. CascadeType.FJERN

Som navnet antyder, fjerner fjernelsen den række, der svarer til enheden fra databasen og også fra den vedvarende kontekst.

CascadeType.FJERN spreder fjernelsesoperationen fra forælder til underordnet enhed.Svarende til JPA'er CascadeType.REMOVE, vi har CascadeType.DELETE, der er specifikt for dvale. Der er ingen forskel mellem de to.

Nu er det tid til at teste CascadeType.Remove:

@Test offentlig ugyldig nårParentRemovedThenChildRemoved () {int personId; Person person = buildPerson ("devender"); Adresse-adresse = buildAddress (person); person.setAddresses (Arrays.asList (adresse)); session.persist (person); session.flush (); personId = person.getId (); session.clear (); Person gemtPersonEntity = session.find (Person.class, personId); session.remove (savedPersonEntity); session.flush (); }

Når vi kører ovenstående testtilfælde, ser vi følgende SQL:

Dvaletilstand: slet fra adresse hvor id =? Dvaletilstand: slet fra person, hvor id =?

Det adresse forbundet med person blev også fjernet som et resultat af CascadeType FJERN.

3.5. CascadeType.DETACH

Afmonteringsoperationen fjerner enheden fra den vedvarende kontekst. Når vi bruger CascaseType.DETACH, den underordnede enhed fjernes også fra den vedvarende kontekst.

Lad os se det i aktion:

@Test offentlig ugyldig nårParentDetachedThenChildDetached () {Person person = buildPerson ("devender"); Adresse-adresse = buildAddress (person); person.setAddresses (Arrays.asList (adresse)); session.persist (person); session.flush (); assertThat (session.contains (person)). isTrue (); assertThat (session.contains (adresse)). isTrue (); session.detach (person); assertThat (session.contains (person)). isFalse (); assertThat (session.contains (address)). isFalse (); }

Her kan vi se det efter løsrivelse person, ingen af ​​dem person heller ikke adresse findes i den vedvarende sammenhæng.

3.6. CascadeType.LÅSE

Uintuitivt, CascadeType.LOCK vedhæfter enheden og dens tilknyttede underenhed igen med den vedvarende kontekst igen.

Lad os se testsagen for at forstå CascadeType.LOCK:

@Test offentlig ugyldig nårDetachedAndLockedThenBothReattached () {Person person = buildPerson ("devender"); Adresse-adresse = buildAddress (person); person.setAddresses (Arrays.asList (adresse)); session.persist (person); session.flush (); assertThat (session.contains (person)). isTrue (); assertThat (session.contains (adresse)). isTrue (); session.detach (person); assertThat (session.contains (person)). isFalse (); assertThat (session.contains (address)). isFalse (); session.unwrap (Session.class) .buildLockRequest (ny LockOptions (LockMode.NONE)) .lock (person); assertThat (session.contains (person)). isTrue (); assertThat (session.contains (adresse)). isTrue (); }

Som vi kan se, når du bruger CascadeType.LOCK, vi tilknyttede enheden person og dens tilknyttede adresse tilbage til den vedvarende kontekst.

3.7. CascadeType.OPDATER

Opdater operationer genlæse værdien af ​​en given forekomst fra databasen. I nogle tilfælde ændrer vi muligvis en forekomst efter vedvarende i databasen, men senere er vi nødt til at fortryde disse ændringer.

I den slags scenarier kan dette være nyttigt. Når vi bruger denne operation med CascadeType OPDATER, den underordnede enhed genindlæses også fra databasen, når den overordnede enhed opdateres.

For bedre forståelse, lad os se en test case for CascadeType.REFRESH:

@Test offentlig ugyldig nårParentRefreshedThenChildRefreshed () {Person person = buildPerson ("devender"); Adresse-adresse = buildAddress (person); person.setAddresses (Arrays.asList (adresse)); session.persist (person); session.flush (); person.setName ("Devender Kumar"); address.setHouseNumber (24); session.refresh (person); assertThat (person.getName ()). isEqualTo ("devender"); assertThat (address.getHouseNumber ()). er EqualTo (23); }

Her foretog vi nogle ændringer i de gemte enheder person og adresse. Når vi opdaterer person enhed, den adresse bliver også opdateret.

3.8. CascadeType.REPLIKAT

Replikeringsfunktionen bruges, når vi har mere end en datakilde, og vi vil have dataene synkroniserede. Med CascadeType.REPLIKAT, en synkroniseringshandling udbreder sig også til underordnede enheder, når den udføres på den overordnede enhed.

Lad os nu teste CascadeType.REPLIKAT:

@Test offentligt ugyldigt nårParentReplicatedThenChildReplicated () {Person person = buildPerson ("devender"); person.setId (2); Adresse-adresse = buildAddress (person); address.setId (2); person.setAddresses (Arrays.asList (adresse)); session.unwrap (Session.class) .replicate (person, ReplicationMode.OVERWRITE); session.flush (); assertThat (person.getId ()). er lig med (2); assertThat (address.getId ()). er EqualTo (2); }

På grund af CascadeTypeREPLIKAT, når vi replikerer person enhed, derefter dens tilknyttede adresse bliver også replikeret med den identifikator, vi indstiller.

3.9. CascadeType.SAVE_UPDATE

CascadeType.SAVE_UPDATE formerer den samme operation til den tilknyttede underordnede enhed. Det er nyttigt, når vi bruger Dvale-specifikke operationer som f.eks gem, opdater, og gem eller opdater.

Lad os se CascadeType.SAVE_UPDATE i aktion:

@Test offentlig ugyldig nårParentSavedThenChildSaved () {Person person = buildPerson ("devender"); Adresse-adresse = buildAddress (person); person.setAddresses (Arrays.asList (adresse)); session.saveOrUpdate (person); session.flush (); }

På grund af CascadeType.SAVE_UPDATE, når vi kører ovenstående testsag, så kan vi se, at person og adresse begge blev reddet. Her er den resulterende SQL:

Dvaletilstand: indsæt i personværdier (navn, id) (?,?) Dvaletilstand: indsæt i adresse (by, husnummer, person_id, gade, zipCode, id) værdier (?,?,?,?,?,?)

4. Konklusion

I denne artikel diskuterede vi cascading og de forskellige muligheder for kaskadetype, der er tilgængelige i JPA og Hibernate.

Kildekoden til artiklen er tilgængelig på GitHub.