Siklus Hidup Entitas Hibernasi

1. Ikhtisar

Setiap entitas Hibernate secara alami memiliki siklus proses dalam kerangka kerja - baik dalam status sementara, terkelola, terlepas, atau dihapus.

Sangat penting untuk memahami keadaan ini pada tingkat konseptual dan teknis agar dapat menggunakan Hibernasi dengan benar.

Untuk mempelajari tentang berbagai metode Hibernate yang berhubungan dengan entitas, lihat salah satu tutorial kami sebelumnya.

2. Metode Pembantu

Sepanjang tutorial ini, kami akan secara konsisten menggunakan beberapa metode helper:

  • HibernateLifecycleUtil.getManagedEntities (sesi) - kami akan menggunakannya untuk mendapatkan semua entitas terkelola dari penyimpanan internal Sesi
  • DirtyDataInspector.getDirtyEntities () - kita akan menggunakan metode ini untuk mendapatkan daftar semua entitas yang ditandai sebagai 'kotor'
  • HibernateLifecycleUtil.queryCount (query) - metode praktis untuk melakukan count (*) query terhadap database yang disematkan

Semua metode pembantu di atas diimpor secara statis agar lebih mudah dibaca. Anda dapat menemukan penerapannya di proyek GitHub yang ditautkan di akhir artikel ini.

3. Semuanya Tentang Konteks Ketekunan

Sebelum masuk ke topik siklus hidup entitas, pertama-tama, kita perlu memahami konteks persistensi .

Sederhananya, konteks persistensi berada di antara kode klien dan penyimpanan data . Ini adalah area pementasan di mana data persisten diubah menjadi entitas, siap dibaca dan diubah oleh kode klien.

Secara teoritis, konteks persistensi merupakan implementasi dari pola Satuan Kerja. Itu melacak semua data yang dimuat, melacak perubahan data itu, dan bertanggung jawab untuk pada akhirnya menyinkronkan setiap perubahan kembali ke database di akhir transaksi bisnis.

JPA EntityManager dan Hibernate's Session merupakan implementasi dari konsep konteks persistensi . Sepanjang artikel ini, kami akan menggunakan Sesi Hibernasi untuk mewakili konteks ketekunan.

Status siklus hidup entitas hibernasi menjelaskan bagaimana entitas terkait dengan konteks persistensi , seperti yang akan kita lihat selanjutnya.

4. Entitas yang Dikelola

Entitas yang dikelola adalah representasi dari baris tabel database (meskipun baris tersebut belum harus ada di database).

Ini dikelola oleh Sesi yang sedang berjalan , dan setiap perubahan yang dibuat padanya akan dilacak dan disebarkan ke database secara otomatis .

The Sesi baik beban entitas dari database atau re-atase entitas terpisah. Kami akan membahas entitas yang terpisah di bagian 5.

Mari kita amati beberapa kode untuk mendapatkan klarifikasi.

Aplikasi sampel kami mendefinisikan satu entitas, kelas FootballPlayer . Saat memulai, kami akan menginisialisasi penyimpanan data dengan beberapa contoh data:

+-------------------+-------+ | Name | ID | +-------------------+-------+ | Cristiano Ronaldo | 1 | | Lionel Messi | 2 | | Gianluigi Buffon | 3 | +-------------------+-------+

Katakanlah kami ingin mengganti nama Buffon sejak awal - kami ingin menggunakan nama lengkapnya Gianluigi Buffon alih-alih Gigi Buffon.

Pertama, kita perlu memulai unit kerja kita dengan mendapatkan Sesi:

Session session = sessionFactory.openSession();

Di lingkungan server, kami dapat memasukkan Sesi ke kode kami melalui proxy yang sadar konteks. Prinsipnya tetap sama: kita membutuhkan Sesi untuk merangkum transaksi bisnis unit kerja kita.

Selanjutnya, kami akan menginstruksikan Sesi kami untuk memuat data dari penyimpanan persisten:

assertThat(getManagedEntities(session)).isEmpty(); List players = s.createQuery("from FootballPlayer").getResultList(); assertThat(getManagedEntities(session)).size().isEqualTo(3); 

Ketika kami pertama kali mendapatkan Session , toko konteks persisten adalah kosong, seperti yang ditunjukkan oleh pertama kami menegaskan pernyataan.

Selanjutnya, kami menjalankan kueri yang mengambil data dari database, membuat representasi entitas dari data, dan akhirnya mengembalikan entitas untuk kami gunakan.

Secara internal, Sesi melacak semua entitas yang dimuatnya di penyimpanan konteks persisten. Dalam kasus kami, penyimpanan internal Sesi akan berisi 3 entitas setelah kueri.

Sekarang mari kita ganti nama Gigi:

Transaction transaction = session.getTransaction(); transaction.begin(); FootballPlayer gigiBuffon = players.stream() .filter(p -> p.getId() == 3) .findFirst() .get(); gigiBuffon.setName("Gianluigi Buffon"); transaction.commit(); assertThat(getDirtyEntities()).size().isEqualTo(1); assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Gianluigi Buffon");

4.1. Bagaimana cara kerjanya?

Saat panggilan untuk melakukan transaksi commit () atau flush () , Sesi akan menemukan entitas kotor apa pun dari daftar pelacakannya dan menyinkronkan status tersebut ke database.

Perhatikan bahwa kita tidak perlu memanggil metode apa pun untuk memberi tahu Sesi bahwa kita mengubah sesuatu di entitas kita - karena ini adalah entitas yang dikelola, semua perubahan disebarkan ke database secara otomatis.

Entitas yang dikelola selalu merupakan entitas yang persisten - ia harus memiliki pengenal database, meskipun representasi baris database belum dibuat, misalnya pernyataan INSERT menunggu akhir dari unit kerja.

Lihat bab tentang entitas sementara di bawah.

5. Entitas Terpisah

Sebuah entitas terpisah hanya sebuah entitas POJO biasa yang identitasnya berkorespondensi nilai untuk baris database. Perbedaan dari entitas terkelola adalah tidak dilacak lagi oleh konteks persistensi apa pun .

Entitas bisa terlepas saat Sesi yang digunakan untuk memuatnya ditutup, atau saat kita memanggil Session.evict (entitas) atau Session.clear () .

Mari kita lihat di kode:

FootballPlayer cr7 = session.get(FootballPlayer.class, 1L); assertThat(getManagedEntities(session)).size().isEqualTo(1); assertThat(getManagedEntities(session).get(0).getId()).isEqualTo(cr7.getId()); session.evict(cr7); assertThat(getManagedEntities(session)).size().isEqualTo(0);

Konteks persistensi kami tidak akan melacak perubahan dalam entitas yang terpisah:

cr7.setName("CR7"); transaction.commit(); assertThat(getDirtyEntities()).isEmpty();

Session.merge (entity) /Session.update (entity) dapat melampirkan (kembali) sesi :

FootballPlayer messi = session.get(FootballPlayer.class, 2L); session.evict(messi); messi.setName("Leo Messi"); transaction.commit(); assertThat(getDirtyEntities()).isEmpty(); transaction = startTransaction(session); session.update(messi); transaction.commit(); assertThat(getDirtyEntities()).size().isEqualTo(1); assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Leo Messi");

Untuk referensi tentang Session.merge () dan Session.update () lihat di sini.

5.1. Bidang Identitas Adalah Yang Penting

Mari kita lihat logika berikut:

FootballPlayer gigi = new FootballPlayer(); gigi.setId(3); gigi.setName("Gigi the Legend"); session.update(gigi);

Dalam contoh di atas, kami telah membuat instance entitas dengan cara biasa melalui konstruktornya. Kami telah mengisi bidang dengan nilai dan kami telah menetapkan identitas menjadi 3, yang sesuai dengan identitas data persisten milik Gigi Buffon. Memanggil update () memiliki efek yang persis sama seperti jika kita telah memuat entitas dari konteks persistensi lain .

Faktanya, Sesi tidak membedakan dari mana entitas yang dilampirkan kembali berasal.

Ini adalah skenario yang cukup umum dalam aplikasi web untuk membangun entitas yang terpisah dari nilai bentuk HTML.

Sejauh menyangkut Sesi , entitas yang terlepas hanyalah entitas biasa yang nilai identitasnya sesuai dengan data yang persisten.

Be aware that the example above just serves a demo purpose. and we need to know exactly what we're doing. Otherwise, we could end up with null values across our entity if we just set the value on the field we want to update, leaving the rest untouched (so, effectively null).

6. Transient Entity

A transient entity is simply an entity object that has no representation in the persistent store and is not managed by any Session.

A typical example of a transient entity would be instantiating a new entity via its constructor.

To make a transient entity persistent, we need to call Session.save(entity) or Session.saveOrUpdate(entity):

FootballPlayer neymar = new FootballPlayer(); neymar.setName("Neymar"); session.save(neymar); assertThat(getManagedEntities(session)).size().isEqualTo(1); assertThat(neymar.getId()).isNotNull(); int count = queryCount("select count(*) from Football_Player where name="Neymar""); assertThat(count).isEqualTo(0); transaction.commit(); count = queryCount("select count(*) from Football_Player where name="Neymar""); assertThat(count).isEqualTo(1);

As soon as we execute Session.save(entity), the entity is assigned an identity value and becomes managed by the Session. However, it might not yet be available in the database as the INSERT operation might be delayed until the end of the unit of work.

7. Deleted Entity

An entity is in a deleted (removed) state if Session.delete(entity) has been called, and the Session has marked the entity for deletion. The DELETE command itself might be issued at the end of the unit of work.

Let's see it in the following code:

session.delete(neymar); assertThat(getManagedEntities(session).get(0).getStatus()).isEqualTo(Status.DELETED);

However, notice that the entity stays in the persistent context store until the end of the unit of work.

8. Conclusion

Konsep konteks persistensi sangat penting untuk memahami siklus hidup entitas Hibernate. Kami telah memperjelas siklus hidup dengan melihat contoh kode yang menunjukkan setiap status.

Seperti biasa, kode yang digunakan dalam artikel ini dapat ditemukan di GitHub.