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ély–Cí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.