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

Jawaban:

1615

Apa pun cara akan menambahkan entitas ke PersistenceContext, perbedaannya adalah apa yang Anda lakukan dengan entitas setelahnya.

Persist mengambil instance entitas, menambahkannya ke konteks dan membuat instance itu dikelola (yaitu pembaruan masa depan untuk entitas akan dilacak).

Gabungkan mengembalikan contoh terkelola yang statusnya digabungkan. Itu mengembalikan sesuatu yang ada di PersistenceContext atau menciptakan instance baru dari entitas Anda. Bagaimanapun, itu akan menyalin negara dari entitas yang disediakan, dan mengembalikan salinan yang dikelola. Contoh yang Anda lewati tidak akan dikelola (perubahan apa pun yang Anda lakukan tidak akan menjadi bagian dari transaksi - kecuali jika Anda memanggil penggabungan lagi). Anda dapat melalui penggunaan instance yang dikembalikan (dikelola satu).

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 kira-kira setara, tetapi ada beberapa situasi di mana Anda ingin menggunakan Skenario 2.

Mike
sumber
3
@dma_k: Sepertinya Anda menggunakan Hibernate. Saya kurang terbiasa dengan Hibernate daripada JPA - tetapi di JPA jika Anda memanggil EntityManager.persist () dan mengirimkan entitas terpisah Anda akan: a) mendapatkan EntityExistsException segera atau b) mendapatkan PersistenceException lain pada waktu flush / commit. Mungkin saya salah paham pertanyaannya di sini?
Mike
49
Jawaban ini dapat ditingkatkan jika itu juga mencakup kasus-kasus di mana entitas yang digabungkan / bertahan sudah ada dalam konteks persisten (atau setidaknya membuatnya lebih jelas bahwa itu hanya menggambarkan perilaku ketika entitas yang ada / digabung tidak ada)
Henry
2
Apakah satu metode lebih performan? Mungkin mergesalinan penuh dari suatu objek sebelum mengelolanya memiliki kinerja yang baik?
Kevin Meredith
2
Bagaimana dengan id? Jika saya punya @GeneratedIdbisakah saya mendapatkannya di skenario 2?
rascio
7
Mike: "Gabung membuat contoh baru ...": ini tidak selalu benar. Jika EntityManager menemukan entitas yang sudah dikelola dalam konteksnya, ia mengembalikan instance ini (setelah memperbarui bidang). Harap edit jawaban Anda, maka saya akan memilihnya.
Heri
181

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

(diedit untuk memperluas informasi perbedaan)

bertahan:

  • Masukkan register baru ke basis data
  • Lampirkan objek ke manajer entitas.

menggabungkan:

  • Temukan objek terlampir dengan id yang sama dan perbarui.
  • Jika ada perbarui dan kembalikan objek yang sudah dilampirkan.
  • Jika tidak ada, masukkan register baru ke basis data.

bertahan () efisiensi:

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

tahan () semantik:

  • Itu memastikan bahwa Anda memasukkan dan tidak memperbarui secara tidak sengaja.

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 terlampir untuk register apa pun di manajer 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 gabungan MySQL () bisa seefisien bertahan () menggunakan panggilan ke INSERT dengan opsi ON DUPLICATE KEY UPDATE, JPA adalah pemrograman tingkat yang sangat tinggi dan Anda tidak dapat menganggap ini akan terjadi di mana-mana.

Josep Panadero
sumber
Dapatkah Anda menyebutkan kasus di mana itu tidak berlaku untuk mengganti em.persist(x)dengan x = em.merge(x)?
Aaron Digulla
20
persist () dapat melempar EntityExistsException. Jika Anda ingin memastikan bahwa kode Anda melakukan penyisipan dan bukan pembaruan data yang harus Anda gunakan tetap ada.
Josep Panadero
1
merge()juga bisa melemparEntityExistsException
Sean
1
@Tidak Ada yang bisa karena itu adalah RuntimeException, tetapi tidak disebutkan di Javadoc.
Martin
154

Jika Anda menggunakan generator yang ditetapkan, menggunakan gabungan alih-alih bertahan dapat menyebabkan pernyataan SQL yang berlebihan , sehingga memengaruhi kinerja.

Selain itu, memanggil penggabungan untuk entitas yang dikelola juga merupakan kesalahan karena entitas yang dikelola secara otomatis dikelola oleh Hibernate dan statusnya disinkronkan dengan catatan database dengan mekanisme pemeriksaan kotor setelah membilas Konteks Persistence .

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

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

Monitor Hibernate yang saat ini terpasang entitas. Tetapi agar suatu entitas dikelola, ia harus dalam status entitas yang benar.

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

Transisi status entitas JPA

Atau jika Anda menggunakan API khusus Hibernate:

Transisi status entitas hibernasi

Seperti diilustrasikan oleh diagram di atas, suatu entitas dapat berada di salah satu dari empat negara berikut:

  • Baru (sementara)

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

    Untuk bertahan, kita perlu secara eksplisit memanggil EntityManager#persistmetode 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 paling bertanggung jawab terakhir, selama waktu Sessionflush saat ini.

  • Terpisah

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

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

    • Pemasangan kembali

      Hibernate (tapi bukan JPA 2.1) mendukung pemasangan kembali melalui metode pembaruan Session #. Sesi Hibernate hanya dapat mengaitkan satu objek Entitas untuk baris database yang diberikan. Ini karena Konteks Persistensi bertindak sebagai cache dalam 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 Sesi Hibernasi saat ini.

    • Penggabungan

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

  • Dihapus

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

Vlad Mihalcea
sumber
dapatkah Anda melihat stackoverflow.com/questions/46214322/… ?
gstackoverflow
@ gstackoverflow Jawaban yang Anda dapatkan adalah yang benar. Untuk detail lebih lanjut, lihat artikel ini atau buku saya, Java Persistence Kinerja Tinggi .
Vlad Mihalcea
Dengan demikian tidak ada kemungkinan untuk mengubah urutan operasi untuk orphanremoval = benar?
gstackoverflow
Artikel Anda tentang urutan operasi dalam kasus biasa. Pertanyaan saya spesifik untuk orphanRemoval
gstackoverflow
1
Faktanya adalah tidak mungkin menjelaskan hibernate dengan diagram seperti itu. Mengapa Anda tidak bisa menyiram sesi setelah melepaskan? Apa yang terjadi ketika Anda mencoba menyelamatkan entitas yang sudah ada? Mengapa perilaku siram itu berbeda untuk menyelamatkan dan bertahan? Ada 1000 pertanyaan seperti itu, yang tidak seorang pun memiliki logika yang jelas.
GingerBeer
37

Saya perhatikan bahwa ketika saya menggunakan em.merge, saya mendapat SELECTpernyataan untuk setiap INSERT, bahkan ketika tidak ada bidang yang dihasilkan JPA untuk saya - bidang kunci utama adalah UUID yang saya tetapkan sendiri. Saya beralih ke em.persist(myEntityObject)dan mendapat INSERTpernyataan saat itu.

Kapal Sarah
sumber
3
Masuk akal karena Anda menetapkan ID dan wadah JPA tidak tahu dari mana Anda mendapatkannya. Ada kemungkinan (kecil) bahwa objek sudah ada dalam database, misalnya dalam skenario di mana beberapa aplikasi menulis ke database yang sama.
Aaron Digulla
Saya menghadapi masalah serupa dengan merge(). Saya memiliki database PostgreSQL dengan tampilan rumit : tampilan agregat data dari beberapa tabel (tabel memiliki struktur yang identik tetapi nama yang berbeda). Jadi JPA coba lakukan merge(), tetapi sebenarnya JPA pertama kali dibuat SELECT(database karena pengaturan tampilan dapat mengembalikan beberapa catatan dengan kunci primer yang sama dari tabel yang berbeda!), Kemudian JPA (Hibernate adalah implementasi) gagal: ada beberapa catatan dengan kunci yang sama ( org.hibernate.HibernateException: More than one row with the given identifier was found). Dalam kasus saya persist()membantu saya.
flaz14
29

Spesifikasi JPA mengatakan hal berikut tentang persist().

Jika X adalah objek yang terpisah, EntityExistsExceptionmungkin dilemparkan ketika operasi bertahan dipanggil, atau yang EntityExistsExceptionlain PersistenceExceptionmungkin dilemparkan pada waktu siram atau komit.

Jadi menggunakan persist()akan cocok ketika objek tidak harus menjadi objek yang terpisah. Anda mungkin lebih suka kode dilemparkanPersistenceException sehingga gagal cepat.

Meskipun spesifikasinya tidak jelas , persist()mungkin mengatur @GeneratedValue @Iduntuk objek. merge()Namun harus memiliki objek dengan yang @Idsudah dibuat.

Raedwald
sumber
5
+1 untuk " merge()namun harus memiliki objek dengan yang @Id sudah dibuat . " Setiap kali EntityManager tidak menemukan nilai untuk bidang ID objek, itu tetap (dimasukkan) ke dalam DB.
Omar
Saya tidak mengerti ini lebih dulu karena saya tidak jelas di negara bagian. Semoga ini bisa membantu seseorang seperti yang terjadi pada saya. docs.jboss.org/hibernate/core/3.6/reference/en-US/html/…
RBz
1
@GeneratedValue tidak memiliki implikasi yang berbeda untuk menggabungkan () dan bertahan ()
SandeepGodara
17

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

Mengembalikan contoh yang dikelola selain dari entitas asli adalah bagian penting dari proses penggabungan. Jika instance entitas dengan pengidentifikasi yang sama sudah ada dalam konteks persistensi, penyedia akan menimpa statusnya dengan keadaan entitas yang sedang digabungkan, tetapi versi terkelola yang ada harus dikembalikan ke klien sehingga dapat bekas. Jika penyedia tidak memperbarui instance Karyawan dalam konteks persistensi, setiap referensi ke instance tersebut akan menjadi tidak konsisten dengan keadaan baru yang digabungkan.

Ketika menggabungkan () dipanggil pada entitas baru, itu berperilaku serupa dengan operasi bertahan (). Itu menambahkan entitas ke konteks kegigihan, tetapi alih-alih menambahkan instance entitas asli, ia menciptakan salinan baru dan mengelola instance itu sebagai gantinya. Salinan yang dibuat oleh operasi gabungan () tetap ada seolah-olah metode bertahan () dipanggil di atasnya.

Di hadapan hubungan, operasi gabungan () akan berusaha memperbarui entitas yang dikelola untuk menunjuk ke versi terkelola dari entitas yang direferensikan oleh entitas terpisah. Jika entitas memiliki hubungan dengan objek yang tidak memiliki identitas persisten, hasil dari operasi gabungan tidak ditentukan. Beberapa penyedia mungkin mengizinkan salinan terkelola untuk menunjuk ke objek non-persisten, sedangkan yang lain mungkin langsung melemparkan pengecualian. Operasi penggabungan () dapat opsional mengalir dalam kasus-kasus ini untuk mencegah pengecualian terjadi. Kami akan membahas cascading operasi gabungan () di bagian ini. Jika entitas yang digabungkan menunjuk ke entitas yang dihapus, pengecualian IllegalArgumentException akan dilempar.

Hubungan malas memuat adalah kasus khusus dalam operasi penggabungan. Jika hubungan pemuatan malas tidak dipicu pada suatu entitas sebelum terlepas, hubungan itu akan diabaikan ketika entitas tersebut bergabung. Jika hubungan dipicu saat dikelola dan kemudian disetel ke nol saat entitas dilepaskan, versi entitas terkelola juga akan memiliki hubungan yang dihapus selama penggabungan. "

Semua informasi di atas diambil dari "Pro JPA 2 Mastering the Java ™ Persistence API" oleh Mike Keith dan Merrick Schnicariol. Bab 6. Bagian detasemen dan penggabungan. Buku ini sebenarnya adalah buku kedua yang dikhususkan untuk JPA oleh penulis. Buku baru ini memiliki banyak informasi baru dari yang sebelumnya. Saya benar-benar merekomendasikan untuk membaca buku ini untuk orang-orang yang akan terlibat secara serius dengan JPA. Saya minta maaf karena secara anonim mengirimkan jawaban pertama saya.

Khurshed Salimov
sumber
17

Ada beberapa perbedaan lagi di antara mergedan persist(saya akan sebutkan lagi yang sudah diposting di sini):

D1. mergetidak membuat entitas yang lulus dikelola, melainkan mengembalikan contoh lain yang dikelola. persistdi sisi lain akan membuat entitas yang lulus 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 suatu entitas dan kemudian memutuskan untuk bertahan kembali, Anda dapat melakukannya hanya dengan bertahan (), karena mergeakan melempar IllegalArgumentException.

D3. Jika Anda memutuskan untuk mengurus ID Anda secara manual (misalnya dengan menggunakan UUID), maka suatu merge operasi akan memicu SELECTkueri berikutnya untuk mencari entitas yang ada dengan ID itu, sementara persistmungkin tidak memerlukan kueri tersebut.

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

Andrei I
sumber
8

Saya mendapatkan pengecualian lazyMemuat pada entitas saya karena saya mencoba mengakses koleksi malas yang ada di sesi.

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 meringankan ini, saya memperbarui entitas yang sama di controller saya dan meneruskannya ke jsp saya, meskipun saya bayangkan ketika saya disimpan kembali dalam sesi itu juga akan dapat diakses SessionScopedan tidak membuangLazyLoadingException , modifikasi contoh 2:

Yang berikut ini berhasil bagi 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!!
pemain logix
sumber
7

Saya menemukan penjelasan ini dari dokumen Hibernate yang mencerahkan, karena mengandung kasus penggunaan:

Penggunaan dan semantik gabungan () tampaknya membingungkan bagi pengguna baru. Pertama, selama Anda tidak mencoba menggunakan status objek yang dimuat dalam satu manajer entitas di manajer entitas baru lainnya, Anda tidak perlu menggunakan gabungan () sama sekali . Beberapa aplikasi secara keseluruhan tidak akan pernah menggunakan metode ini.

Biasanya menggabungkan () digunakan dalam skenario berikut:

  • Aplikasi memuat objek di manajer entitas pertama
  • objek diteruskan ke lapisan presentasi
  • beberapa modifikasi dilakukan pada objek
  • objek dilewatkan kembali ke lapisan logika bisnis
  • aplikasi tetap menggunakan modifikasi ini dengan memanggil penggabungan () di manajer entitas kedua

Berikut adalah semantik gabungan yang tepat ():

  • jika ada instance yang dikelola dengan pengidentifikasi yang sama saat ini terkait dengan konteks kegigihan, salin keadaan objek yang diberikan ke instance yang dikelola
  • jika tidak ada instance yang dikelola saat ini terkait dengan konteks persistence, cobalah memuatnya dari database, atau buat instance yang dikelola baru
  • contoh terkelola dikembalikan
  • contoh yang diberikan tidak menjadi terkait dengan konteks kegigihan, itu tetap terpisah dan biasanya dibuang

Dari: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html

Ray Hulha
sumber
6

Menjelajahi jawaban ada beberapa detail yang hilang tentang `Cascade 'dan pembuatan id. Lihat pertanyaan

Juga, perlu disebutkan bahwa Anda dapat memiliki Cascadeanotasi terpisah untuk digabungkan dan bertahan: Cascade.MERGEdan Cascade.PERSISTyang akan diperlakukan sesuai dengan metode yang digunakan.

Speknya adalah teman Anda;)

Ioannis Deligiannis
sumber
6

JPA tidak dapat disangkal merupakan penyederhanaan besar dalam domain aplikasi perusahaan yang dibangun pada platform Java. Sebagai pengembang yang harus mengatasi seluk-beluk kacang entitas lama di J2EE saya melihat dimasukkannya JPA di antara spesifikasi Java EE sebagai lompatan besar ke depan. Namun, saat menggali lebih dalam ke detail JPA saya menemukan hal-hal yang tidak begitu mudah. Dalam artikel ini saya berurusan dengan perbandingan penggabungan dan metode EntityManager yang perilakunya yang tumpang tindih dapat menyebabkan kebingungan tidak hanya untuk pemula. Selanjutnya saya mengusulkan generalisasi yang melihat kedua metode sebagai kasus khusus dari metode yang lebih umum digabungkan.

Entitas yang bertahan lama

Berbeda dengan metode penggabungan, metode bertahan ini cukup mudah dan intuitif. Skenario yang paling umum dari penggunaan metode bertahan dapat diringkas sebagai berikut:

"Sebuah instance yang baru dibuat dari kelas entitas diteruskan ke metode bertahan. Setelah metode ini kembali, entitas dikelola dan direncanakan untuk dimasukkan ke dalam database. Ini dapat terjadi pada atau sebelum transaksi dilakukan atau ketika metode flush dipanggil. Jika entitas merujuk entitas lain melalui hubungan yang ditandai dengan strategi kaskade PERSIST, prosedur ini juga berlaku untuknya. "

masukkan deskripsi gambar di sini

Spesifikasi lebih ke rincian, namun mengingatnya tidak penting karena rincian ini mencakup situasi yang kurang lebih eksotis saja.

Menggabungkan entitas

Dibandingkan dengan bertahan, deskripsi perilaku gabungan tidak begitu sederhana. Tidak ada skenario utama, seperti dalam kasus bertahan, dan seorang programmer harus mengingat semua skenario untuk menulis kode yang benar. Tampak bagi saya bahwa desainer JPA ingin memiliki beberapa metode yang perhatian utamanya akan menangani entitas terpisah (sebagai kebalikan dari metode bertahan yang berhubungan dengan entitas yang baru dibuat terutama.) Tugas utama metode penggabungan adalah untuk mentransfer negara dari suatu entitas yang tidak dikelola (diteruskan sebagai argumen) ke mitra yang dikelola dalam konteks persistensi. Namun, tugas ini membagi lebih jauh ke dalam beberapa skenario yang memperburuk kejelasan perilaku keseluruhan metode.

Alih-alih mengulangi paragraf dari spesifikasi JPA saya telah menyiapkan diagram alir yang secara skematis menggambarkan perilaku metode gabungan:

masukkan deskripsi gambar di sini

Jadi, kapan saya harus menggunakan bertahan dan kapan bergabung?

bertahan

  • Anda ingin metode selalu membuat entitas baru dan tidak pernah memperbarui entitas. Jika tidak, metode melempar pengecualian sebagai konsekuensi dari pelanggaran keunikan kunci primer.
  • Proses batch, menangani entitas dengan cara stateful (lihat pola Gateway).
  • Optimalisasi kinerja

menggabungkan

  • Anda ingin metode memasukkan atau memperbarui entitas dalam database.
  • Anda ingin menangani entitas tanpa kewarganegaraan (objek transfer data dalam layanan)
  • Anda ingin menyisipkan entitas baru yang mungkin memiliki referensi ke entitas lain yang mungkin tetapi mungkin belum dibuat (hubungan harus ditandai MERGE). Misalnya, memasukkan foto baru dengan referensi ke album baru atau yang sudah ada sebelumnya.
Amit Gujarathi
sumber
Apa perbedaan antara E yang dikelola dan Apakah PC memuat versi E yang dikelola?
GingerBeer
5

Skenario X:

Tabel: Spitter (Satu), Tabel: Spittles (Banyak) (Spittles adalah Pemilik hubungan dengan FK: spitter_id)

Skenario ini menghasilkan penghematan: The Spitter dan kedua Spittles seolah-olah dimiliki oleh Same Spitter.

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.addSpittle(spittle3); // <--persist     
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

Skenario Y:

Ini akan menyelamatkan Spitter, akan menyelamatkan 2 Spittles Tetapi mereka tidak akan merujuk Spitter yang sama!

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.save(spittle3); // <--merge!!       
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!
George Papatheodorou
sumber
1
Spitter adalah objek yang diambil dari buku "Spring in Action" Edisi ketiga oleh Graig Walls. Spitter adalah orang yang mengatakan sesuatu dan ludahnya adalah apa yang sebenarnya mereka katakan. Jadi Spitter memiliki banyak spittle berarti dia memiliki daftar String.
George Papatheodorou
1
Anda bisa menggunakan contoh yang sedikit lebih mudah dibaca tanpa membaca Spring in Action ...
wonderb0lt
1
Anda sebenarnya tidak perlu tahu apa itu ludah atau ludah karena di atas tertulis bahwa Spitter adalah meja, ludah adalah meja lain yang memiliki .. ini dan itu ...
George Papatheodorou
3

Pengamatan lain:

merge()hanya akan peduli dengan id yang dibuat secara otomatis (diuji pada IDENTITYdan SEQUENCE) ketika catatan dengan id tersebut sudah ada di tabel Anda. Dalam hal ini merge()akan mencoba memperbarui catatan. Namun, jika id tidak ada atau tidak cocok dengan catatan yang ada, merge()akan sepenuhnya mengabaikannya dan meminta db untuk mengalokasikan yang baru. Ini terkadang merupakan sumber dari banyak bug. Jangan gunakanmerge() untuk memaksa id untuk catatan baru.

persist()di sisi lain tidak akan pernah membiarkan Anda memberikan id padanya. Ini akan segera gagal. Dalam kasus saya, ini:

Disebabkan oleh: org.hibernate.PersistentObjectException: entitas terpisah dilewatkan untuk bertahan

hibernate-jpa javadoc memiliki petunjuk:

Lempar : javax.persistence.EntityExistsException - jika entitas sudah ada. (Jika entitas sudah ada, EntityExistsException dapat dilemparkan ketika operasi persisten dipanggil, atau EntityExistsException atau PersistenceException lain dapat dilemparkan pada waktu siram atau komit.)

yuranos
sumber
2
Jika Anda tidak menggunakan ID yang dibuat secara otomatis, Anda harus memberikan ID Entitas baru Anda secara manual. persist()tidak akan mengeluh bahwa ia memiliki ID, itu hanya mengeluh ketika sesuatu dengan ID yang sama sudah ada dalam database.
jam
1

Anda mungkin datang ke sini untuk meminta nasihat tentang kapan harus menggunakan bertahan dan kapan harus menggunakan gabungan . Saya pikir itu tergantung situasinya: seberapa mungkin Anda perlu membuat catatan baru dan seberapa sulit untuk mengambil data yang ada.

Mari kita anggap Anda dapat menggunakan kunci alami / pengidentifikasi.

  • Data perlu dipertahankan, tetapi sesekali catatan ada dan pembaruan diperlukan. Dalam hal ini Anda bisa mencoba bertahan dan jika itu melempar EntityExistsException, Anda mencarinya dan menggabungkan data:

    coba {entitasManager.persist (entitas)}

    catch (pengecualian EntityExistsException) {/ * mengambil dan menggabungkan * /}

  • Data yang bertahan perlu diperbarui, tetapi sesekali tidak ada catatan untuk data tersebut. Dalam hal ini Anda mencarinya, dan lakukan terus jika entitas hilang:

    entitas = entitasManager.find (kunci);

    if (entitas == null) {entityManager.persist (entitas); }

    lain {/ * gabung * /}

Jika Anda tidak memiliki kunci alami / pengidentifikasi, Anda akan memiliki waktu lebih sulit untuk mengetahui apakah entitas itu ada atau tidak, atau bagaimana cara mencarinya.

Gabungan dapat ditangani dengan dua cara, juga:

  1. Jika perubahan biasanya kecil, terapkan pada entitas yang dikelola.
  2. Jika perubahan sering terjadi, salin ID dari entitas yang bertahan, serta data yang tidak diubah. Kemudian panggil EntityManager :: merge () untuk mengganti konten lama.
Peter Willems
sumber
0

persist (entitas) harus digunakan dengan entitas yang benar-benar baru, untuk menambahkannya ke DB (jika entitas sudah ada di DB akan ada lemparan EntityExistsException).

penggabungan (entitas) harus digunakan, untuk mengembalikan entitas ke konteks persistensi jika entitas dilepaskan dan diubah.

Mungkin tetap menghasilkan pernyataan INSERT sql dan menggabungkan pernyataan UPDATE sql (tapi saya tidak yakin).

Krystian
sumber
Ini salah. Jika Anda memanggil gabungan (e) pada e baru, itu harus dipertahankan.
Pedro Lamarão
Dari spesifikasi JPA versi 2.1, bagian 3.2.7.1, butir kedua: "Jika X adalah instance entitas baru, instance entitas dikelola baru X 'dibuat dan keadaan X disalin ke instance entitas dikelola baru X'."
Pedro Lamarão