Tinjauan Jenis JPA / Hibernate Cascade

1. Pengenalan

Dalam tutorial ini, kita akan membincangkan apa itu cascading dalam JPA / Hibernate. Kemudian kita akan membahas pelbagai jenis lata yang ada, bersama dengan semantiknya.

2. Apa itu Cascading?

Hubungan entiti sering bergantung pada kewujudan entiti lain - contohnya, hubungan Orang - Alamat . Tanpa Orang , entiti Alamat tidak mempunyai makna tersendiri. Apabila kami menghapus entiti Orang , entiti Alamat kami juga harus dihapus.

Cascading adalah cara untuk mencapainya. Ketika kita melakukan beberapa tindakan pada entitas sasaran, tindakan yang sama akan diterapkan pada entitas yang terkait.

2.1. Jenis lata JPA

Semua operasi lata khas JPA diwakili oleh enum javax.persistence.CascadeType yang mengandungi entri:

  • SEMUA
  • PERIBADI
  • KELEBIHAN
  • HAPUSKAN
  • RUJUKAN
  • DETACH

2.2. Jenis Cascade Hibernate

Hibernate menyokong tiga Jenis Cascade tambahan bersama dengan yang ditentukan oleh JPA. Jenis Cascade khusus Hibernate ini terdapat di org.hibernate.annotations.CascadeType :

  • REPLIKASI
  • JIMAT_UPDATE
  • KUNCI

3. Perbezaan Antara Jenis lata

3.1. Jenis Cascade . SEMUA

Cascade.ALL menyebarkan semua operasi - termasuk operasi khusus Hibernate - dari ibu bapa ke entiti anak.

Mari kita lihat dalam contoh:

@Entity public class Person { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; @OneToMany(mappedBy = "person", cascade = CascadeType.ALL) private List addresses; }

Perhatikan bahawa dalam persatuan OneToMany , kami telah menyebut jenis lata dalam anotasi.

Sekarang, mari kita lihat Alamat entiti yang berkaitan :

@Entity public class Address { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String street; private int houseNumber; private String city; private int zipCode; @ManyToOne(fetch = FetchType.LAZY) private Person person; }

3.2. Jenis Cascade . PERIBADI

Operasi berterusan menjadikan contoh sementara berterusan. CascadeType Berterusan merambat yang berterusan operasi daripada ibu bapa kepada entiti kanak-kanak . Apabila kita menyelamatkan entiti orang tersebut , entiti alamat juga akan disimpan.

Mari lihat kes ujian untuk operasi berterusan:

@Test public void whenParentSavedThenChildSaved() { Person person = new Person(); Address address = new Address(); address.setPerson(person); person.setAddresses(Arrays.asList(address)); session.persist(person); session.flush(); session.clear(); }

Apabila kita menjalankan kes ujian di atas, kita akan melihat SQL berikut:

Hibernate: insert into Person (name, id) values (?, ?) Hibernate: insert into Address ( city, houseNumber, person_id, street, zipCode, id) values (?, ?, ?, ?, ?, ?)

3.3. Jenis Cascade . KELEBIHAN

Operasi penggabungan menyalin keadaan objek yang diberikan ke objek berterusan dengan pengenal yang sama. CascadeType.MERGE menyebarkan operasi penggabungan dari ibu bapa ke entiti anak .

Mari kita uji operasi penggabungan:

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

Apabila kita menjalankan kes ujian di atas, operasi penggabungan menghasilkan SQL berikut:

Hibernate: select address0_.id as id1_0_0_, address0_.city as city2_0_0_, address0_.houseNumber as houseNum3_0_0_, address0_.person_id as person_i6_0_0_, address0_.street as street4_0_0_, address0_.zipCode as zipCode5_0_0_ from Address address0_ where address0_.id=? Hibernate: select person0_.id as id1_1_0_, person0_.name as name2_1_0_ from Person person0_ where person0_.id=? Hibernate: update Address set city=?, houseNumber=?, person_id=?, street=?, zipCode=? where id=? Hibernate: update Person set name=? where id=?

Di sini, kita dapat melihat bahawa operasi penggabungan memuatkan kedua alamat dan entiti orang pertama dan kemudian mengemas kini kedua-duanya sebagai hasil daripada CascadeType MERGE .

3.4. CascadeType.HAPUSKAN

Seperti namanya, operasi hapus membuang baris yang sesuai dengan entiti dari pangkalan data dan juga dari konteks berterusan.

CascadeType.REMOVE menyebarkan operasi buang dari induk ke entiti anak. Mirip dengan JPA's CascadeType.REMOVE, kami mempunyai CascadeType.DELETE , yang khusus untuk Hibernate . Tidak ada perbezaan antara keduanya.

Sekarang, sudah tiba masanya untuk menguji CascadeType. Keluarkan :

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

Apabila kita menjalankan kes ujian di atas, kita akan melihat SQL berikut:

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

The alamat dikaitkan dengan orang juga tidak perlu lagi dikeluarkan akibat CascadeType REMOVE .

3.5. CascadeType.DETACH

The detach operation removes the entity from the persistent context. When we use CascaseType.DETACH, the child entity will also get removed from the persistent context.

Let's see it in action:

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

Here, we can see that after detaching person, neither person nor address exists in the persistent context.

3.6. CascadeType.LOCK

Unintuitively, CascadeType.LOCK re-attaches the entity and its associated child entity with the persistent context again.

Let's see the test case to understand CascadeType.LOCK:

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

As we can see, when using CascadeType.LOCK, we attached the entity person and its associated address back to the persistent context.

3.7. CascadeType.REFRESH

Refresh operations re-read the value of a given instance from the database. In some cases, we may change an instance after persisting in the database, but later we need to undo those changes.

In that kind of scenario, this may be useful. When we use this operation with CascadeType REFRESH, the child entity also gets reloaded from the database whenever the parent entity is refreshed.

For better understanding, let's see a test case for CascadeType.REFRESH:

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

Here, we made some changes in the saved entities person and address. When we refresh the person entity, the address also gets refreshed.

3.8. CascadeType.REPLICATE

The replicate operation is used when we have more than one data source, and we want the data in sync. With CascadeType.REPLICATE, a sync operation also propagates to child entities whenever performed on the parent entity.

Now, let's test CascadeType.REPLICATE:

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

Because of CascadeTypeREPLICATE, when we replicate the person entity, then its associated address also gets replicated with the identifier we set.

3.9. CascadeType.SAVE_UPDATE

CascadeType.SAVE_UPDATE propagates the same operation to the associated child entity. It's useful when we use Hibernate-specific operations like save, update, and saveOrUpdate.

Let's see CascadeType.SAVE_UPDATE in action:

@Test public void whenParentSavedThenChildSaved() { Person person = buildPerson("devender"); Address address = buildAddress(person); person.setAddresses(Arrays.asList(address)); session.saveOrUpdate(person); session.flush(); }

Kerana CascadeType.SAVE_UPDATE , apabila kita menjalankan kes ujian di atas, maka kita dapat melihat bahawa orang dan alamat keduanya tersimpan. Inilah SQL yang dihasilkan:

Hibernate: insert into Person (name, id) values (?, ?) Hibernate: insert into Address ( city, houseNumber, person_id, street, zipCode, id) values (?, ?, ?, ?, ?, ?)

4. Kesimpulan

Dalam artikel ini, kami membincangkan pilihan lata dan pelbagai jenis lata yang terdapat di JPA dan Hibernate.

Kod sumber untuk artikel tersebut terdapat di GitHub.