A JPA / hibernált kaszkádtípusok áttekintése

1. Bemutatkozás

Ebben az oktatóanyagban megvitatjuk, hogy mi a lépcsőzetes a JPA / Hibernate módban. Ezután kitérünk a rendelkezésre álló különféle kaszkádtípusokra, szemantikájukkal együtt.

2. Mi a lépcsőzetes?

Az entitásviszonyok gyakran egy másik entitás - például a - létezésétől függenek SzemélyCím kapcsolat. A Személy, a Cím az entitásnak nincs saját jelentése. Amikor töröljük a Személy entitás, a mi Cím entitást is törölni kell.

Ennek elérése a lépcsőzetes módszerrel történik. Amikor valamilyen műveletet végrehajtunk a megcélzott entitással, ugyanaz a művelet lesz alkalmazva a társított entitásra is.

2.1. JPA kaszkádtípus

Az összes JPA-specifikus kaszkádműveletet a javax.persistence.CascadeType bejegyzés, amely bejegyzéseket tartalmaz:

  • MINDEN
  • PERSISTA
  • ÖSSZEOLVAD
  • Távolítsa el
  • FRISSÍTÉS
  • LETÖLTENI

2.2. Hibernált kaszkád típus

A hibernálás három további kaszkád-típust támogat a JPA által meghatározottakkal együtt. Ezek a hibernált állapotra jellemző kaszkád típusok itt érhetők el org.hibernate.annotations.CascadeType:

  • MEGISMÉTELNI
  • SAVE_UPDATE
  • ZÁR

3. Különbség a kaszkád típusok között

3.1. CascadeType.MINDEN

Cascade.ALLaz összes műveletet - beleértve a hibernált állapotra vonatkozó műveleteket is - szülőtől gyermek entitássá terjeszti.

Nézzük meg egy példában:

@Entity public class Person {@Id @GeneratedValue (strategy = GenerationType.AUTO) private int id; privát karakterlánc neve; @OneToMany (mappedBy = "person", cascade = CascadeType.ALL) privát lista címek; }

Vegye figyelembe, hogy a Egy a sokhoz egyesületek, a kaszkád típust említettük az annotációban.

Most nézzük meg a társított entitást Cím:

@Entity public class Address {@Id @GeneratedValue (strategy = GenerationType.AUTO) private int id; saját vonós utca; magán int házszám privát String város; privát int irányítószám; @ManyToOne (fetch = FetchType.LAZY) magánszemély; }

3.2. CascadeType.PERSISTA

A persist művelet egy tranziens példányt tartóssá tesz. CascadeType PERSISTA terjeszti a persist műveletet a szülőtől a gyermek entitásig. Amikor elmentjük a személy entitás, az cím entitás is mentésre kerül.

Lássuk a folyamatos működés teszt esetét:

@Test public void whenParentSavedThenChildSaved () {Személy személy = új Személy (); Cím címe = új cím (); address.setPerson (személy); person.setAddresses (Arrays.asList (cím)); munkamenet.ellenőr (személy); session.flush (); session.clear (); }

A fenti teszteset futtatásakor a következő SQL-t látjuk:

Hibernálás: beszúrás a Személy (név, azonosító) értékekbe (?,?) Hibernálás: beszúrás a Cím (város, házszám, személy_azonosító, utca, irányítószám, id) értékekbe (?,?,?,?,?,?)

3.3. CascadeType.ÖSSZEOLVAD

Az egyesítési művelet az adott objektum állapotát az azonos azonosítóval rendelkező állandó objektumra másolja. CascadeType.MERGE az egyesítési műveletet szülőből gyermek entitássá terjeszti.

Teszteljük az egyesítési műveletet:

@Test public void whenParentSavedThenMerged () {int addressId; Személy személy = buildPerson ("devender"); Cím címe = buildAddress (személy); person.setAddresses (Arrays.asList (cím)); munkamenet.ellenőr (személy); session.flush (); addressId = address.getId (); session.clear (); Address savedAddressEntity = session.find (Address.class, addressId); Személy savedPersonEntity = savedAddressEntity.getPerson (); savedPersonEntity.setName ("devender kumar"); savedAddressEntity.setHouseNumber (24); session.merge (savedPersonEntity); session.flush (); }

A fenti teszteset futtatásakor az egyesítési művelet a következő SQL-t generálja:

Hibernálás: válassza a address0_.id címet id1_0_0_, address0_.city mint city2_0_0_, address0_.houseNumber mint houseNum3_0_0_, address0_.person_id as person_i6_0_0_, address0_.street as street4_0_0_, address0_____cím0__cím_cím_0__cím_cím_0__cím__0__cím__0__cím_0__cím__0__cím__0__cím_0__cím_0__cím_cím_0__cím_cím_0__cím_0__cím_cím_0 Hibernálás: válassza a person0_.id nevet id1_1_0_, a személy0_.name nevet2_1_0_ a Person person0_ helyről person0_.id =? Hibernálás: frissítse a címkészletet city = ?, houseNumber = ?, person_id = ?, street = ?, zipCode =? ahol id =? Hibernálás: frissítse a Személykészlet nevét =? ahol id =?

Itt láthatjuk, hogy az egyesítési művelet először mindkettőt betölti cím és személy entitások, majd a rendszer eredményeként mindkettőt frissíti CascadeType MERGE.

3.4. CascadeType.REMOVE

Ahogy a neve is sugallja, az eltávolítási művelet eltávolítja az entitásnak megfelelő sort az adatbázisból és a tartós környezetből is.

CascadeType.REMOVE az eltávolítási műveletet szülőről gyermek entitásra terjeszti.Hasonló a JPA-khoz CascadeType.REMOVE, nekünk van CascadeType.DELETE, amely csak a hibernált állapotra jellemző. Nincs különbség a kettő között.

Itt az ideje tesztelni CascadeType. Távolítsa el:

@Test public void whenParentRemovedThenChildRemoved () {int personId; Személy személy = buildPerson ("devender"); Cím címe = buildAddress (személy); person.setAddresses (Arrays.asList (cím)); munkamenet.ellenőr (személy); session.flush (); personId = személy.getId (); session.clear (); Személy mentettPersonEntity = session.find (Személy.osztály, personId); session.remove (savedPersonEntity); session.flush (); }

A fenti teszteset futtatásakor a következő SQL-t látjuk:

Hibernálás: törlés a címről, ahol id =? Hibernálás: törlés a Személyből, ahol id =?

A cím társul a személy eredményeként is eltávolították CascadeType REMOVE.

3.5. CascadeType.DETACH

A leválasztási művelet eltávolítja az entitást a tartós környezetből. Amikor használjuk CascaseType.DETACH, a gyermek entitás is eltávolításra kerül a tartós kontextusból.

Lássuk működés közben:

@Test public void whenParentDetachedThenChildDetached () {Person person = buildPerson ("devender"); Cím címe = buildAddress (személy); person.setAddresses (Arrays.asList (cím)); munkamenet.ellenőr (személy); session.flush (); assertThat (session.contains (person)). isTrue (); assertThat (session.contains (address)). isTrue (); session.detach (személy); assertThat (session.contains (person)). isFalse (); assertThat (session.contains (address)). isFalse (); }

Itt láthatjuk, hogy leválás után személy, se személy sem cím a perzisztens kontextusban létezik.

3.6. CascadeType.ZÁR

Nem szándékosan, CascadeType.LOCK újra összeköti az entitást és a hozzá tartozó gyermek entitást a tartós kontextussal.

Lássuk a tesztesetet, hogy megértsük CascadeType.LOCK:

@Test public void whenDetachedAndLockedThenBothReattached () {Person person = buildPerson ("devender"); Cím címe = buildAddress (személy); person.setAddresses (Arrays.asList (cím)); munkamenet.ellenőr (személy); session.flush (); assertThat (session.contains (person)). isTrue (); assertThat (session.contains (address)). isTrue (); session.detach (személy); assertThat (session.contains (person)). isFalse (); assertThat (session.contains (address)). isFalse (); session.unwrap (Session.class) .buildLockRequest (új LockOptions (LockMode.NONE)) .lock (személy); assertThat (session.contains (person)). isTrue (); assertThat (session.contains (address)). isTrue (); }

Mint láthatjuk, használatakor CascadeType.LOCK, csatoltuk az entitást személy és társult cím vissza a kitartó kontextusba.

3.7. CascadeType.FRISSÍTÉS

Frissítési műveletek olvassa el újra az adott példány értékét az adatbázisból. Bizonyos esetekben megváltoztathatunk egy példányt, miután fennmaradtunk az adatbázisban, de később ezeket a módosításokat vissza kell vonni.

Ilyen esetekben ez hasznos lehet. Amikor ezt a műveletet a CascadeType programmal használjuk FRISSÍTÉS, a gyermek entitás is újratöltődik az adatbázisból, amikor a szülő entitás frissül.

A jobb megértés érdekében lássunk egy tesztesetet CascadeType.REFRESH:

@Test public void whenParentRefreshedThenChildRefreshed () {Person person = buildPerson ("devender"); Cím címe = buildAddress (személy); person.setAddresses (Arrays.asList (cím)); munkamenet.ellenőr (személy); session.flush (); person.setName ("Devender Kumar"); address.setHouseNumber (24); session.refresh (személy); assertThat (person.getName ()). isEqualTo ("devender"); assertThat (address.getHouseNumber ()). isEqualTo (23); }

Itt néhány változtatást hajtottunk végre a mentett entitásokban személy és cím. Amikor frissítjük a személy entitás, az cím szintén felfrissül.

3.8. CascadeType.REPLICATE

A replikációs műveletet akkor használjuk, ha egynél több adatforrásunk van, és szinkronban szeretnénk az adatokat. Val vel CascadeType.REPLICATE, a szinkronizálási művelet az alárendelt entitásokra is átterjed, amikor csak végrehajtják a szülő entitáson.

Most teszteljünk CascadeType.MEGISMÉTELNI:

@Test public void whenParentReplicatedThenChildReplicated () {Person person = buildPerson ("devender"); person.setId (2); Cím címe = buildAddress (személy); address.setId (2); person.setAddresses (Arrays.asList (cím)); session.unwrap (Session.class) .replicate (személy, ReplicationMode.OVERWRITE); session.flush (); assertThat (person.getId ()). isEqualTo (2); assertThat (address.getId ()). isEqualTo (2); }

Mert CascadeTypeMEGISMÉTELNI, amikor megismételjük a személy entitás, majd társítva cím szintén replikálódik az általunk beállított azonosítóval.

3.9. CascadeType.SAVE_UPDATE

CascadeType.SAVE_UPDATE ugyanazt a műveletet terjeszti a társított gyermek entitásra. Hasznos, ha használjuk Hibernálás-specifikus műveletek, mint pl mentés, frissítés, és saveOrUpdate.

Lássuk CascadeType.SAVE_UPDATE működés közben:

@Test public void whenParentSavedThenChildSaved () {Person person = buildPerson ("devender"); Cím címe = buildAddress (személy); person.setAddresses (Arrays.asList (cím)); session.saveOrUpdate (személy); session.flush (); }

Mert CascadeType.SAVE_UPDATE, amikor a fenti tesztesetet lefuttatjuk, láthatjuk, hogy a személy és cím mindkettőt megmentették. Íme a kapott SQL:

Hibernálás: beszúrás a Személy (név, azonosító) értékekbe (?,?) Hibernálás: beszúrás a Cím (város, házszám, személy_azonosító, utca, irányítószám, id) értékekbe (?,?,?,?,?,?)

4. Következtetés

Ebben a cikkben megvitattuk a lépcsőzést és a különböző kaszkád típusú opciókat, amelyek elérhetők a JPA és a Hibernate rendszerben.

A cikk forráskódja elérhető a GitHubon.