Karena ini adalah pertanyaan yang sangat umum, saya menulis
artikel ini , yang menjadi dasar jawaban ini.
Status entitas
JPA mendefinisikan status entitas berikut:
Baru (sementara)
Objek yang baru dibuat yang belum pernah dikaitkan dengan Hibernate Session
(aliasPersistence Context
) dan tidak dipetakan ke baris tabel basis data apa pun dianggap berada dalam keadaan Baru (Transient).
Untuk bertahan, kita perlu secara eksplisit memanggil EntityManager#persist
metode atau memanfaatkan mekanisme kegigihan transitif.
Gigih (Dikelola)
Entitas persisten telah dikaitkan dengan baris tabel database dan dikelola oleh Konteks Persistence yang sedang berjalan. Setiap perubahan yang dibuat untuk entitas seperti itu akan dideteksi dan disebarkan ke database (selama Sesi flush-time).
Dengan Hibernate, kita tidak lagi harus menjalankan pernyataan INSERT / UPDATE / DELETE. Hibernate menggunakan gaya kerja tulis-balik transaksional dan perubahan disinkronkan pada saat yang paling bertanggung jawab terakhir, selama waktu Session
flush saat ini.
Terpisah
Setelah Konteks Persistensi berjalan saat ini ditutup semua entitas yang sebelumnya dikelola menjadi terlepas. Perubahan yang berurutan tidak akan lagi dilacak dan tidak ada sinkronisasi basis data otomatis yang akan terjadi.
Transisi status entitas
Anda dapat mengubah status entitas menggunakan berbagai metode yang ditentukan oleh EntityManager
antarmuka.
Untuk memahami transisi status entitas JPA dengan lebih baik, pertimbangkan diagram berikut:
Saat menggunakan JPA, untuk menghubungkan kembali entitas yang terpisah ke aktif EntityManager
, Anda dapat menggunakan operasi gabungan .
Saat menggunakan API Hibernate asli, selain dari merge
, Anda dapat memasang kembali entitas terpisah ke Sesi Hibernasi aktif menggunakan metode pembaruan, seperti yang ditunjukkan oleh diagram berikut:
Menggabungkan entitas yang terpisah
Penggabungan akan menyalin status entitas terpisah (sumber) ke instance entitas terkelola (tujuan).
Anggaplah kita telah mempertahankan Book
entitas berikut , dan sekarang entitas terlepas seperti EntityManager
yang digunakan untuk bertahan entitas ditutup:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
Saat entitas dalam keadaan terpisah, kami memodifikasinya sebagai berikut:
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
Sekarang, kami ingin menyebarkan perubahan ke database, sehingga kami dapat memanggil merge
metode:
doInJPA(entityManager -> {
Book book = entityManager.merge(_book);
LOGGER.info("Merging the Book entity");
assertFalse(book == _book);
});
Dan Hibernate akan menjalankan pernyataan SQL berikut:
SELECT
b.id,
b.author AS author2_0_,
b.isbn AS isbn3_0_,
b.title AS title4_0_
FROM
book b
WHERE
b.id = 1
-- Merging the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
Jika entitas penggabungan tidak memiliki padanan dalam arus EntityManager
, snapshot entitas baru akan diambil dari database.
Setelah ada entitas manged, JPA menyalin status entitas terpisah ke entitas yang saat ini dikelola, dan selama Konteks Persistensiflush
, UPDATE akan dihasilkan jika mekanisme pemeriksaan kotor menemukan bahwa entitas yang dikelola telah berubah.
Jadi, ketika menggunakan merge
, instance objek yang dilepaskan akan terus tetap terlepas bahkan setelah operasi penggabungan.
Pemasangan kembali entitas yang terpisah
Hibernasi, tetapi JPA tidak mendukung pemasangan kembali melalui update
metode.
A Hibernate Session
hanya dapat mengaitkan satu objek entitas untuk baris database yang diberikan. Ini karena Konteks Persistensi bertindak sebagai cache di memori (cache level pertama) dan hanya satu nilai (entitas) yang dikaitkan dengan kunci yang diberikan (tipe entitas dan pengidentifikasi basis data).
Suatu entitas dapat disambungkan kembali hanya jika tidak ada objek JVM lainnya (cocok dengan baris database yang sama) yang sudah dikaitkan dengan Hibernate saat ini Session
.
Mengingat kami telah bertahan Book
entitas dan bahwa kami memodifikasinya ketika Book
entitas dalam keadaan terpisah:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
Kami dapat memasang kembali entitas yang terlepas seperti ini:
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.update(_book);
LOGGER.info("Updating the Book entity");
});
Dan Hibernate akan menjalankan pernyataan SQL berikut:
-- Updating the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
The update
metode mengharuskan Anda untuk unwrap
yang EntityManager
ke Hibernate Session
.
Tidak seperti itu merge
, entitas terpisah yang disediakan akan dihubungkan kembali dengan Konteks Persistensi saat ini dan UPDATE dijadwalkan selama flush apakah entitas telah dimodifikasi atau belum.
Untuk mencegah hal ini, Anda dapat menggunakan @SelectBeforeUpdate
anotasi Hibernate yang akan memicu pernyataan SELECT yang mengambil status dimuat yang kemudian digunakan oleh mekanisme pemeriksaan kotor.
@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {
//Code omitted for brevity
}
Waspadai NonUniqueObjectException
Salah satu masalah yang dapat terjadi update
adalah jika Konteks Persistensi sudah berisi referensi entitas dengan id yang sama dan dari jenis yang sama seperti dalam contoh berikut:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
try {
doInJPA(entityManager -> {
Book book = entityManager.find(
Book.class,
_book.getId()
);
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(_book);
});
} catch (NonUniqueObjectException e) {
LOGGER.error(
"The Persistence Context cannot hold " +
"two representations of the same entity",
e
);
}
Sekarang, ketika menjalankan test case di atas, Hibernate akan melempar NonUniqueObjectException
karena yang kedua EntityManager
sudah berisi Book
entitas dengan pengidentifikasi yang sama seperti yang kita lewati update
, dan Persistence Context tidak dapat menampung dua representasi dari entitas yang sama.
org.hibernate.NonUniqueObjectException:
A different object with the same identifier value was already associated with the session : [com.vladmihalcea.book.hpjp.hibernate.pc.Book#1]
at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:651)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:227)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:92)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:682)
at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:674)
Kesimpulan
The merge
metode adalah lebih disukai jika Anda menggunakan penguncian optimis karena memungkinkan Anda untuk mencegah update yang hilang. Untuk detail lebih lanjut tentang topik ini, lihat artikel ini .
Ini update
bagus untuk pembaruan batch karena dapat mencegah pernyataan SELECT tambahan yang dihasilkan oleh merge
operasi, sehingga mengurangi waktu eksekusi pembaruan batch.
refresh()
pada entitas yang terpisah? Melihat melalui spesifikasi 2.0, saya tidak melihat adanya pembenaran; Hanya saja itu tidak diizinkan.