Pertanyaan JPA EntityManager: Mengapa menggunakan persist () over merge ()?


EntityManager.merge() dapat memasukkan objek baru dan memperbarui yang sudah ada.

Mengapa yang ingin digunakan persist() (yang hanya dapat membuat objek baru)?


839
2017-07-01 16:03


asal


Jawaban:


Either way akan menambahkan entitas ke PersistenceContext, perbedaannya adalah dalam apa yang Anda lakukan dengan entitas sesudahnya.

Bertahan mengambil contoh entitas, menambahkannya ke konteks dan membuat instance dikelola (mis. Pembaruan masa depan untuk entitas akan dilacak).

Penggabungan membuat instance baru dari entitas Anda, menyalin negara dari entitas yang disediakan, dan membuat salinan baru dikelola. Instance yang Anda berikan tidak akan dikelola (perubahan apa pun yang Anda lakukan tidak akan menjadi bagian dari transaksi - kecuali Anda memanggil bergabung kembali).

Mungkin contoh kode akan membantu.

MyEntity e = new MyEntity();

// scenario 1
// tran starts
em.persist(e); 
e.setSomeField(someValue); 
// tran ends, and the row for someField is updated in the database

// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue); 
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)

// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue); 
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)

Skenario 1 dan 3 secara kasar setara, tetapi ada beberapa situasi di mana Anda ingin menggunakan Skenario 2.


1455
2017-07-01 18:28



Bertahan dan bergabung adalah untuk dua tujuan yang berbeda (mereka bukan alternatif sama sekali).

(diedit untuk memperluas informasi perbedaan)

bertahan:

  • Masukkan daftar baru ke database
  • Lampirkan objek ke pengelola entitas.

menggabungkan:

  • Temukan objek terlampir dengan ID yang sama dan perbarui.
  • Jika ada pembaruan dan kembalikan objek yang sudah terlampir.
  • Jika tidak ada, masukkan register baru ke database.

persistensi () efisiensi:

  • Ini bisa lebih efisien untuk memasukkan register baru ke database daripada menggabungkan ().
  • Itu tidak menduplikasi objek asli.

tetap () semantik:

  • Ini memastikan bahwa Anda memasukkan dan tidak memperbarui karena kesalahan.

Contoh:

{
    AnyEntity newEntity;
    AnyEntity nonAttachedEntity;
    AnyEntity attachedEntity;

    // Create a new entity and persist it        
    newEntity = new AnyEntity();
    em.persist(newEntity);

    // Save 1 to the database at next flush
    newEntity.setValue(1);

    // Create a new entity with the same Id than the persisted one.
    AnyEntity nonAttachedEntity = new AnyEntity();
    nonAttachedEntity.setId(newEntity.getId());

    // Save 2 to the database at next flush instead of 1!!!
    nonAttachedEntity.setValue(2);
    attachedEntity = em.merge(nonAttachedEntity);

    // This condition returns true
    // merge has found the already attached object (newEntity) and returns it.
    if(attachedEntity==newEntity) {
            System.out.print("They are the same object!");
    }

    // Set 3 to value
    attachedEntity.setValue(3);
    // Really, now both are the same object. Prints 3
    System.out.println(newEntity.getValue());

    // Modify the un attached object has no effect to the entity manager
    // nor to the other objects
    nonAttachedEntity.setValue(42);
}

Cara ini hanya ada 1 objek yang dilampirkan untuk setiap daftar di pengelola entitas.

menggabungkan () untuk entitas dengan id adalah sesuatu seperti:

AnyEntity myMerge(AnyEntity entityToSave) {
    AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
    if(attached==null) {
            attached = new AnyEntity();
            em.persist(attached);
    }
    BeanUtils.copyProperties(attached, entityToSave);

    return attached;
}

Meskipun jika terhubung ke MySQL menggabungkan () bisa seefisien bertahan () menggunakan panggilan untuk INSERT dengan opsi UPDATE KUNCI DUPLICATE, JPA adalah pemrograman tingkat yang sangat tinggi dan Anda tidak dapat menganggap ini akan menjadi kasus di mana-mana.


151
2018-06-11 11:04



Jika Anda menggunakan generator yang ditetapkan, menggunakan penggabungan bukannya bertahan dapat menyebabkan pernyataan SQL yang berlebihan, oleh karena itu mempengaruhi kinerja.

Juga, panggilan gabungan untuk entitas yang dikelola juga merupakan kesalahan karena entitas yang dikelola secara otomatis dikelola oleh Hibernate dan statusnya disinkronkan dengan catatan basis data oleh mekanisme pemeriksaan kotor atas menyiratkan Persistensi Persistensi.

Untuk memahami bagaimana semua ini bekerja, Anda harus terlebih dahulu mengetahui bahwa Hibernate menggeser pola pikir pengembang dari pernyataan SQL menjadi transisi entitas entitas.

Setelah entitas dikelola secara aktif oleh Hibernate, semua perubahan akan secara otomatis disebarkan ke database.

Hibernate memonitor entitas yang terhubung saat ini. Tetapi untuk entitas yang akan dikelola, itu harus dalam keadaan entitas yang benar.

Pertama, kita harus mendefinisikan semua status entitas:

  • Baru (Transien)

    Objek yang baru dibuat yang belum pernah dikaitkan dengan Hibernate Session (a.k.a Persistence Context) dan tidak dipetakan ke baris tabel database apa pun dianggap berada dalam status Baru (Transien).

    Untuk menjadi teguh kita perlu secara eksplisit memanggil EntityManager#persist metode atau memanfaatkan mekanisme persistensi transitif.

  • Persistent (Managed)

    Entitas yang terus-menerus dikaitkan dengan baris tabel basis data dan dikelola oleh Persistensi Konteks berjalan saat ini. Setiap perubahan yang dibuat untuk entitas tersebut akan dideteksi dan disebarkan ke basis data (selama sesi waktu siram). Dengan Hibernate, kita tidak lagi harus mengeksekusi pernyataan INSERT / UPDATE / DELETE. Hibernasi menggunakan a transactional write-behind gaya kerja dan perubahan disinkronisasi pada saat tanggung jawab yang paling terakhir, selama saat ini Session waktu siram.

  • Terpisah

    Setelah Persistensi Persistensi berjalan saat ini ditutup semua entitas yang dikelola sebelumnya menjadi terpisah. Perubahan berurutan tidak lagi dilacak dan tidak ada sinkronisasi basis data otomatis yang akan terjadi.

    Untuk mengaitkan entitas yang terpisah dengan Sesi Hibernasi aktif, Anda dapat memilih salah satu opsi berikut:

    • Penyambungan kembali

      Hibernate (tetapi tidak JPA 2.1) mendukung penyambungan ulang melalui metode pembaruan Sesi #. Sesi Hibernasi hanya dapat mengaitkan satu objek Entitas dengan baris database tertentu. Ini karena Konteks Persistensi bertindak sebagai cache di-memori (tingkat pertama cache) dan hanya satu nilai (entitas) yang terkait dengan kunci tertentu (tipe entitas dan pengidentifikasi basis data). Entitas dapat disambungkan hanya jika tidak ada objek JVM lainnya (yang cocok dengan baris basis data yang sama) yang sudah terkait dengan Sesi Hibernasi saat ini.

    • Penggabungan

    Penggabungan akan menyalin status entitas yang terpisah (sumber) ke instance entitas terkelola (tujuan). Jika entitas penggabungan tidak memiliki padanan dalam Sesi saat ini, yang akan diambil dari database. Instance objek yang terpisah akan terus tetap terpisah bahkan setelah operasi penggabungan.

  • Dihapus

    Meskipun JPA menuntut bahwa entitas yang dikelola hanya diizinkan untuk dihapus, Hibernate juga dapat menghapus entitas terpisah (tetapi hanya melalui panggilan metode # sesi Session). Entitas yang dihapus hanya dijadwalkan untuk dihapus dan pernyataan DELETE database yang sebenarnya akan dieksekusi selama sesi flush-time.

Untuk memahami transisi status JPA yang lebih baik, Anda dapat memvisualisasikan diagram berikut:

enter image description here

Atau jika Anda menggunakan API khusus Hibernate:

enter image description here


108
2018-05-11 13:00



Saya memperhatikan bahwa ketika saya menggunakannya em.merge, Saya mendapat SELECT pernyataan untuk setiap INSERT, bahkan ketika tidak ada bidang yang dibuat JPA untuk saya - bidang kunci utama adalah UUID yang saya tetapkan sendiri. Saya beralih ke em.persist(myEntityObject) dan baru saja INSERT pernyataan itu.


37
2018-01-18 21:14



Spesifikasi JPA mengatakan hal berikut tentang persist().

Jika X adalah objek terpisah, EntityExistsException mungkin dilempar ketika yang bertahan   operasi dipanggil, atau EntityExistsExceptionatau yang lain PersistenceException dapat dilemparkan saat menyiram atau melakukan waktu.

Jadi menggunakan persist() akan cocok ketika objek seharusnya tidak menjadi objek terpisah. Anda mungkin lebih memilih untuk melontarkan kode PersistenceException jadi gagal dengan cepat.

Meskipun spesifikasi tidak jelas, persist() mungkin mengatur @GeneratedValue  @Id untuk sebuah objek. merge() Namun harus memiliki objek dengan @Id sudah dihasilkan.


27
2018-03-11 17:23



Beberapa detail lebih lanjut tentang penggabungan yang akan membantu Anda menggunakan gabungan lebih permanen:

Mengembalikan instance terkelola selain entitas asli adalah bagian penting dari penggabungan   proses. Jika turunan entitas dengan pengidentifikasi yang sama sudah ada dalam konteks persistensi, maka   penyedia akan menimpa negaranya dengan keadaan entitas yang sedang digabung, tetapi yang dikelola   versi yang sudah ada harus dikembalikan ke klien agar bisa digunakan. Jika penyedia tidak   perbarui instance Karyawan dalam konteks persistensi, referensi apa pun ke instance tersebut akan menjadi   tidak konsisten dengan negara baru yang bergabung.

Ketika menggabungkan () dipanggil pada entitas baru, ia berperilaku serupa dengan operasi persist (). Ia menambahkan   entitas untuk konteks persistensi, tetapi bukannya menambahkan instance entitas asli, itu menciptakan yang baru   salin dan kelola instance itu sebagai gantinya. Salinan yang dibuat oleh operasi gabungan () dipertahankan   seolah-olah metode yang bertahan () dipanggil di situ.

Di hadapan hubungan, operasi penggabungan () akan mencoba memperbarui entitas yang dikelola   untuk menunjuk ke versi terkelola dari entitas yang direferensikan oleh entitas yang terpisah. Jika entitas memiliki   hubungan dengan objek yang tidak memiliki identitas tetap, hasil dari operasi gabungan   tidak terdefinisi. Beberapa penyedia mungkin mengizinkan salinan terkelola untuk menunjuk ke objek yang tidak terus-menerus,   sedangkan yang lain mungkin melempar pengecualian segera. Operasi penggabungan () dapat secara opsional   mengalir dalam kasus-kasus ini untuk mencegah pengecualian terjadi. Kami akan mencakup cascading dari gabungan ()   operasi nanti di bagian ini. Jika entitas yang digabungkan menunjuk ke entitas yang dihapus, sebuah   Pengecualian IllegalArgumentException akan dibuang.

Hubungan pemuatan-malas adalah kasus khusus dalam operasi penggabungan. Jika malas-loading   hubungan tidak terpicu pada suatu entitas sebelum menjadi terpisah, hubungan itu akan terjadi   diabaikan ketika entitas digabung. Jika hubungan dipicu saat dikelola dan kemudian disetel ke nol saat entitas dipisahkan, versi terkelola dari entitas juga akan memiliki hubungan yang dijernihkan selama penggabungan. "

Semua informasi di atas diambil dari "Pro JPA 2 Menguasai Java Persistence API" oleh Mike Keith dan Merrick Schnicariol. Bab 6. Bagian detasemen dan penggabungan. Buku ini sebenarnya adalah buku kedua yang ditujukan untuk JPA oleh penulis. Buku baru ini memiliki banyak informasi baru, lalu yang pertama. Saya sangat menyarankan untuk membaca buku ini untuk orang-orang yang akan terlibat serius dengan JPA. Saya minta maaf karena secara anonim memposting jawaban pertama saya.


16
2017-10-05 13:00



Ada beberapa perbedaan lagi merge dan persist (Saya akan menyebutkan lagi yang sudah diposting di sini):

D1. merge tidak membuat entitas yang berhasil dikelola, tetapi mengembalikan contoh lain yang dikelola. persist di sisi lain akan membuat entitas yang berhasil dikelola:

//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);

//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);

D2. Jika Anda menghapus entitas dan kemudian memutuskan untuk melanjutkan kembali entitas, Anda dapat melakukannya hanya dengan persist (), karena merge akan melempar IllegalArgumentException.

D3. Jika Anda memutuskan untuk berhati-hati secara manual dari ID Anda (misalnya dengan menggunakan UUID), maka a merge  operasi akan memicu selanjutnya SELECT pertanyaan untuk mencari entitas yang ada dengan ID itu, sementara persist mungkin tidak membutuhkan pertanyaan itu.

D4. Ada beberapa kasus ketika Anda tidak mempercayai kode yang memanggil kode Anda, dan untuk memastikan bahwa tidak ada data yang diperbarui, melainkan dimasukkan, Anda harus menggunakan persist.


15
2017-12-02 14:13



Saya mulai malas melakukan pengecualian pada entitas saya karena saya mencoba mengakses koleksi yang memuat malas yang sedang berlangsung.

Apa yang akan saya lakukan adalah dalam permintaan terpisah, mengambil entitas dari sesi dan kemudian mencoba mengakses koleksi di halaman jsp saya yang bermasalah.

Untuk mengatasi hal ini, saya memperbarui entitas yang sama di pengontrol saya dan meneruskannya ke jsp saya, meskipun saya membayangkan ketika saya menyimpannya kembali di sesi itu juga akan dapat diakses SessionScope dan tidak melempar a LazyLoadingException, modifikasi dari contoh 2:

Hal-hal berikut ini berhasil untuk saya:

// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"

//access e from jsp and it will work dandy!!

8
2017-10-20 16:13



Akan melalui jawaban ada beberapa rincian yang hilang tentang `Cascade 'dan generasi id. Lihat pertanyaan

Juga, perlu disebutkan bahwa Anda dapat memiliki terpisah Cascade anotasi untuk menggabungkan dan bertahan: Cascade.MERGE dan Cascade.PERSIST yang akan dirawat sesuai dengan metode yang digunakan.

Spesifikasinya adalah teman Anda;)


6
2017-07-17 15:41