Prehľad typov kaskády JPA / Hibernate

1. Úvod

V tomto výučbe si povieme, čo je kaskádové usporiadanie v JPA / Hibernate. Potom vysvetlíme rôzne dostupné typy kaskád spolu s ich sémantikou.

2. Čo je kaskádové riadenie?

Vzťahy medzi entitami často závisia od existencie inej entity - napríklad OsobaAdresa vzťah. Bez Osoba, Adresa entita nemá vlastný význam. Keď odstránime Osoba subjekt, náš Adresa subjekt by mal byť tiež odstránený.

Kaskádovanie je spôsob, ako to dosiahnuť. Keď vykonáme nejakú akciu na cieľovej entite, rovnaká akcia sa použije aj na pridruženú entitu.

2.1. Kaskádový typ JPA

Všetky kaskádové operácie špecifické pre JPA predstavuje javax.persistence.CascadeType enum obsahujúci záznamy:

  • VŠETKY
  • PERSISTA
  • ZLÚČIŤ
  • ODSTRÁNIŤ
  • OBNOVIŤ
  • DETACH

2.2. Kaskádový typ hibernácie

Hibernate podporuje ďalšie tri kaskádové typy spolu s typmi špecifikovanými JPA. Tieto typy kaskády špecifické pre režim dlhodobého spánku sú k dispozícii v org.hibernate.annotations.CascadeType:

  • REPLIKÁCIA
  • SAVE_UPDATE
  • ZÁMOK

3. Rozdiel medzi kaskádovými typmi

3.1. CascadeType.VŠETKY

Cascade.ALLšíri všetky operácie - vrátane operácií špecifických pre režim dlhodobého spánku - z nadradenej na podradenú entitu.

Pozrime sa na príklade:

@Entity verejná trieda Osoba {@Id @GeneratedValue (strategy = GenerationType.AUTO) private int id; súkromné ​​meno reťazca; @OneToMany (mappedBy = "osoba", cascade = CascadeType.ALL) súkromné ​​adresy adries; }

Všimnite si, že v OneToMany asociácie, v anotácii sme spomenuli kaskádový typ.

Teraz sa pozrime na pridruženú entitu Adresa:

@Entity Public Class Address {@Id @GeneratedValue (strategy = GenerationType.AUTO) private int id; súkromná ulica String; private int houseNumber; súkromné ​​mesto String; private int zipCode; @ManyToOne (fetch = FetchType.LAZY) súkromná osoba; }

3.2. CascadeType.PERSISTA

Vďaka operácii pretrvávania je prechodná inštancia trvalá. CascadeType PERSISTA propaguje pretrvávajúcu operáciu z rodiča na podradenú entitu. Keď uložíme osoba subjekt, adresa entita sa tiež uloží.

Pozrime sa na testovací prípad pretrvávajúcej operácie:

@Test public void whenParentSavedThenChildSaved () {Osoba osoba = nová Osoba (); Adresa adresa = nová adresa (); address.setPerson (person); person.setAddresses (Arrays.asList (adresa)); session.persist (osoba); session.flush (); session.clear (); }

Keď spustíme vyššie uvedený testovací prípad, uvidíme nasledujúci SQL:

Hibernate: vložte do hodnoty Person (name, id) values ​​(?,?) Hibernate: vložte do hodnoty Address (city, houseNumber, person_id, street, zipCode, id) (?,?,?,?,?,?)

3.3. CascadeType.ZLÚČIŤ

Operácia zlúčenia skopíruje stav daného objektu na trvalý objekt s rovnakým identifikátorom. CascadeType.MERGE šíri operáciu zlúčenia z nadradenej do podradenej entity.

Vyskúšajme operáciu zlúčenia:

@Test public void whenParentSavedThenMerged () {int addressId; Osoba osoba = buildPerson ("devender"); Adresa adresa = buildAddress (osoba); person.setAddresses (Arrays.asList (adresa)); session.persist (osoba); session.flush (); addressId = address.getId (); session.clear (); Adresa savedAddressEntity = session.find (Address.class, addressId); Osoba savedPersonEntity = savedAddressEntity.getPerson (); savedPersonEntity.setName ("devender kumar"); savedAddressEntity.setHouseNumber (24); session.merge (savedPersonEntity); session.flush (); }

Keď spustíme vyššie uvedený testovací prípad, operácia zlúčenia vygeneruje nasledujúci SQL:

Hibernácia: vyberte address0_.id ako id1_0_0_, address0_.city ako city2_0_0_, address0_.houseNumber ako houseNum3_0_0_, address0_.person_id ako person_i6_0_0_, address0_.street ako ulica4_0_0_, adresa0__z_0_adresa__0_zip_0_adresa_0_zip_0_adresa_0_zip_0_adresa_0_zip_0_adresa_0_zip_0_od0 Hibernácia: vyberte person0_.id ako id1_1_0_, person0_.name ako meno2_1_0_ z Person person0_ where person0_.id =? Hibernate: update Set set city = ?, houseNumber = ?, person_id = ?, street = ?, zipCode =? kde id =? Hibernate: update Person set name =? kde id =?

Tu vidíme, že operácia zlúčenia najskôr načíta obe adresa a osoba subjekty a následne aktualizuje obidva v dôsledku CascadeType ZLÚČIŤ.

3.4. CascadeType. ODSTRÁNIŤ

Ako naznačuje názov, operácia odstránenia odstráni riadok zodpovedajúci entite z databázy a tiež z pretrvávajúceho kontextu.

CascadeType. ODSTRÁNIŤ šíri operáciu odstránenia z nadradenej entity na podradenú.Podobné ako JPA CascadeType. ODSTRÁNIŤ, máme CascadeType.DELETE, ktorý je špecifický pre režim dlhodobého spánku. Nie je medzi nimi žiadny rozdiel.

Teraz je čas na testovanie CascadeType. Odstrániť:

@Test public void whenParentRemovedThenChildRemoved () {int personId; Osoba osoba = buildPerson ("devender"); Adresa adresa = buildAddress (osoba); person.setAddresses (Arrays.asList (adresa)); session.persist (osoba); session.flush (); personId = person.getId (); session.clear (); Osoba savedPersonEntity = session.find (Person.class, personId); session.remove (savedPersonEntity); session.flush (); }

Keď spustíme vyššie uvedený testovací prípad, uvidíme nasledujúci SQL:

Hibernate: delete from Address where id =? Hibernate: delete from Person where id =?

The adresa spojené s osoba tiež odstránené v dôsledku CascadeType ODSTRÁNIŤ.

3.5. CascadeType.DETACH

Operácia odpojenia odstráni entitu z pretrvávajúceho kontextu. Keď použijeme CascaseType.DETACH, podradená entita bude tiež odstránená z pretrvávajúceho kontextu.

Pozrime sa na to v akcii:

@Test public void whenParentDetachedThenChildDetached () {Osoba osoba = buildPerson ("devender"); Adresa adresa = buildAddress (person); person.setAddresses (Arrays.asList (adresa)); session.persist (osoba); session.flush (); assertThat (session.contains (person)). isTrue (); assertThat (session.contains (address)). isTrue (); session.detach (osoba); assertThat (session.contains (person)). isFalse (); assertThat (session.contains (address)). isFalse (); }

Tu to vidíme po odpojení osoba, ani jeden osoba ani adresa existuje v pretrvávajúcom kontexte.

3.6. CascadeType.ZÁMOK

Neintuitívne, CascadeType.LOCK znova pripojí entitu a jej pridruženú podradenú entitu k pretrvávajúcemu kontextu.

Pozrime sa na testovací prípad, aby sme ho pochopili CascadeType.LOCK:

@Test public void whenDetachedAndLockedThenBothReattached () {Person person = buildPerson ("devender"); Adresa adresa = buildAddress (person); person.setAddresses (Arrays.asList (adresa)); session.persist (osoba); session.flush (); assertThat (session.contains (person)). isTrue (); assertThat (session.contains (address)). isTrue (); session.detach (osoba); assertThat (session.contains (person)). isFalse (); assertThat (session.contains (adresa)). isFalse (); session.unwrap (Session.class) .buildLockRequest (nové LockOptions (LockMode.NONE)) .lock (osoba); assertThat (session.contains (person)). isTrue (); assertThat (session.contains (address)). isTrue (); }

Ako vidíme, pri použití CascadeType.LOCK, sme pripojili entitu osoba a s tým spojené adresa späť do pretrvávajúcich súvislostí.

3.7. CascadeType.OBNOVIŤ

Obnoviť operácie znovu načítať hodnotu danej inštancie z databázy. V niektorých prípadoch môžeme inštanciu zmeniť po pretrvaní v databáze, ale neskôr je potrebné tieto zmeny vrátiť späť.

V takomto prípade to môže byť užitočné. Keď použijeme túto operáciu s CascadeType OBNOVIŤ, podradená entita sa tiež znovu načíta z databázy vždy, keď sa nadradená entita obnoví.

Pre lepšie pochopenie si ukážeme testovací prípad pre CascadeType.REFRESH:

@Test public void whenParentRefreshedThenChildRefreshed () {Osoba osoba = buildPerson ("devender"); Adresa adresa = buildAddress (osoba); person.setAddresses (Arrays.asList (adresa)); session.persist (osoba); session.flush (); person.setName ("Devender Kumar"); address.setHouseNumber (24); session.refresh (osoba); assertThat (person.getName ()). isEqualTo ("devender"); assertThat (address.getHouseNumber ()). isEqualTo (23); }

Tu sme vykonali niekoľko zmien v uložených entitách osoba a adresa. Keď osviežime osoba subjekt, adresa tiež dostane občerstvenie.

3.8. CascadeType.REPLICATE

Operácia replikácie sa používa, keď máme viac ako jeden zdroj údajov a chceme, aby boli údaje synchronizované. S CascadeType.REPLICATE, operácia synchronizácie sa rozšíri aj na podradené entity, kedykoľvek sa vykoná na nadradenej entite.

Teraz poďme otestovať CascadeType.REPLIKÁCIA:

@Test public void whenParentReplicatedThenChildReplicated () {Osoba osoba = buildPerson ("devender"); person.setId (2); Adresa adresa = buildAddress (osoba); address.setId (2); person.setAddresses (Arrays.asList (adresa)); session.unwrap (Session.class) .replicate (person, ReplicationMode.OVERWRITE); session.flush (); assertThat (person.getId ()). isEqualTo (2); assertThat (address.getId ()). isEqualTo (2); }

Kvôli CascadeTypeREPLIKÁCIA, keď replikujeme osoba subjekt, potom jeho pridružený adresa tiež dostane replikáciu s identifikátorom, ktorý sme nastavili.

3.9. CascadeType.SAVE_UPDATE

CascadeType.SAVE_UPDATE šíri rovnakú operáciu pridruženej podradenej entite. Je to užitočné, keď používame Operácie špecifické pre režim dlhodobého spánku ako uložiť, aktualizovať, a saveOrUpdate.

Pozrime sa CascadeType.SAVE_UPDATE v akcii:

@Test public void whenParentSavedThenChildSaved () {Osoba osoba = buildPerson ("devender"); Adresa adresa = buildAddress (person); person.setAddresses (Arrays.asList (adresa)); session.saveOrUpdate (osoba); session.flush (); }

Kvôli CascadeType.SAVE_UPDATE, keď spustíme vyššie uvedený testovací prípad, potom môžeme vidieť, že osoba a adresa obaja boli zachránení. Tu je výsledný SQL:

Hibernate: vložte do hodnoty Person (name, id) values ​​(?,?) Hibernate: vložte do hodnoty Address (city, houseNumber, person_id, street, zipCode, id) values ​​(?,?,?,?,?,?)

4. Záver

V tomto článku sme diskutovali o kaskádovaní a rôznych možnostiach typu kaskády dostupných v JPA a Hibernate.

Zdrojový kód článku je k dispozícii na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found