Apa perbedaan antara berbagai metode penyimpanan di Hibernate?

199

Hibernate memiliki beberapa metode yang, dengan satu atau lain cara, mengambil objek Anda dan memasukkannya ke dalam basis data. Apa perbedaan di antara mereka, kapan harus menggunakan yang mana, dan mengapa tidak ada hanya satu metode cerdas yang tahu kapan harus menggunakan apa?

Metode yang telah saya identifikasi sejauh ini adalah:

  • save()
  • update()
  • saveOrUpdate()
  • saveOrUpdateCopy()
  • merge()
  • persist()
Henrik Paul
sumber

Jawaban:

117

Inilah pemahaman saya tentang metode ini. Terutama ini didasarkan pada API meskipun karena saya tidak menggunakan semua ini dalam praktek.

saveOrUpdate Call, simpan atau perbarui tergantung pada beberapa pemeriksaan. Misalnya jika tidak ada pengidentifikasi, simpan disebut. Kalau tidak, pembaruan disebut.

simpan Tetap suatu entitas. Akan menetapkan pengidentifikasi jika tidak ada. Jika ada, itu pada dasarnya melakukan pembaruan. Mengembalikan ID yang dihasilkan dari entitas.

perbarui Upaya untuk mempertahankan entitas menggunakan pengenal yang ada. Jika tidak ada pengidentifikasi, saya yakin ada pengecualian.

saveOrUpdateCopy Ini sudah usang dan seharusnya tidak lagi digunakan. Sebaliknya ada ...

bergabung Sekarang di sinilah pengetahuan saya mulai goyah. Yang penting di sini adalah perbedaan antara entitas sementara, terpisah dan gigih. Untuk info lebih lanjut tentang status objek, lihat di sini . Dengan save & update, Anda berurusan dengan objek yang persisten. Mereka ditautkan ke Sesi sehingga Hibernate tahu apa yang telah berubah. Tetapi ketika Anda memiliki objek sementara, tidak ada sesi yang terlibat. Dalam kasus ini, Anda harus menggunakan gabungan untuk pembaruan dan bertahan untuk menyimpan.

bertahan Seperti disebutkan di atas, ini digunakan pada objek sementara. Itu tidak mengembalikan ID yang dihasilkan.

Lee Theobald
sumber
22
Saya ingin menerima ini sebagai jawabannya, tetapi satu hal masih belum jelas: karena save () kembali pada pembaruan (), jika item tersebut ada, bagaimana perbedaannya dari saveOrUpdate () dalam prakteknya?
Henrik Paul
Di mana itu ditentukan, bahwa save akan bekerja pada instance yang terpisah?
jrudolph
2
Jika deskripsi Anda tentang penggabungan / bertahan hanya penting pada objek sementara maka ini masuk akal dan cocok dengan cara kami menggunakan hibernasi. Perhatikan juga penggabungan sering kali memiliki keterbatasan kinerja dibandingkan dengan pembaruan karena tampaknya melakukan pengambilan ekstra untuk beberapa jenis pemeriksaan integritas.
Martin Dale Lyness
1
Jawaban oleh jrudolph di bawah ini lebih akurat.
azerole
2
Mengingat hibernasi mungkin mengetahui status objek, mengapa kita harus melakukan ini secara manual saat menulis program. Seharusnya hanya ada satu metode simpan.
masterxilo
116
╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
    METHOD                TRANSIENT                      DETACHED            
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
                     sets id if doesn't         sets new id even if object   
    save()         exist, persists to db,        already has it, persists    
                  returns attached object     to DB, returns attached object 
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
                     sets id on object                    throws             
   persist()       persists object to DB            PersistenceException     
                                                                             
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
                                                                             
   update()              Exception                persists and reattaches    
                                                                             
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
                copy the state of object in      copy the state of obj in    
    merge()        DB, doesn't attach it,    ║      DB, doesn't attach it,    
                  returns attached object         returns attached object    
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
                                                                             
saveOrUpdate()║           as save()                       as update()         
                                                                             
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝
Sergii Shevchyk
sumber
updateobjek sementara tidak masalah, saya tidak mendapatkan pengecualian.
GMsoF
Yang saya tahu, kita tidak bisa bertahan sementara dengan cara baik. Saya pikir perbedaan bisa antara terpisah dan gigih. Tolong perbaiki saya.
Ram
ada banyak kesalahan di sini ... misalnya 1) ´save () ´ tidak mengembalikan "objek terlampir", ia mengembalikan ´id´; 2) ´persist () ´ tidak dijamin untuk menyetel ´id ', juga tidak "menolak objek untuk DB"; ...
Eugen Labun
67
  • Lihat Forum Hibernate untuk penjelasan tentang perbedaan halus antara bertahan dan menyimpan. Sepertinya perbedaannya adalah waktu pernyataan INSERT akhirnya dieksekusi. Karena save mengembalikan pengidentifikasi, pernyataan INSERT harus dijalankan secara instan terlepas dari kondisi transaksi (yang umumnya merupakan hal yang buruk). Persist tidak akan menjalankan pernyataan di luar transaksi yang sedang berjalan hanya untuk menetapkan pengenal. Simpan / Tetap sama-sama bekerja pada instance sementara , yaitu instance yang belum memiliki pengidentifikasi dan karena itu belum disimpan dalam DB.

  • Perbarui dan Gabungkan keduanya berfungsi pada instance terpisah , yaitu instance yang memiliki entri yang sesuai dalam DB tetapi saat ini tidak dilampirkan pada (atau dikelola oleh) Sesi. Perbedaan di antara mereka adalah apa yang terjadi pada instance yang diteruskan ke fungsi. pembaruan mencoba memasang kembali instance, yang berarti bahwa tidak boleh ada instance lain dari entitas persisten yang melekat pada Sesi sekarang, jika tidak ada pengecualian yang dilemparkan. menggabungkan , namun, cukup salin semua nilai ke instance persisten di Sesi (yang akan dimuat jika saat ini tidak dimuat). Objek input tidak berubah. Jadi penggabungan lebih umum daripada pembaruan, tetapi dapat menggunakan lebih banyak sumber daya.

jrudolph
sumber
masukkan pernyataan masih tidak terjadi jika Anda memiliki generator Id Anda sendiri
kommradHomer
Jadi dalam kasus objek terpisah, penggabungan akan memicu pemilihan sedangkan pembaruan tidak akan?
Gab
1
save() - If an INSERT has to be executed to get the identifier, then this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is problematic in a long-running conversation with an extended Session/persistence context.Bisakah Anda memberi tahu saya bagaimana penyisipan dapat terjadi di luar sesi dan mengapa itu buruk?
Erran Morad
Penafian: Saya sudah lama tidak menggunakan hibernate. IMO masalahnya adalah ini: tanda tangan dan kontrak save () mengharuskan save mengembalikan pengidentifikasi untuk objek baru. Bergantung pada strategi generasi id yang Anda pilih, pengidentifikasi dihasilkan oleh DB ketika nilai INSERTdiedit. Akibatnya, dalam kasus tersebut, Anda tidak dapat mengembalikan pengidentifikasi sekarang tanpa membuatnya dan untuk menghasilkan Anda harus menjalankan INSERT sekarang . Karena, transaksi jangka panjang tidak berjalan sekarang tetapi hanya pada komit, satu-satunya cara untuk mengeksekusi INSERTsekarang adalah menjalankannya di luar tx.
jrudolph
12

Tautan ini menjelaskan dengan baik:

http://www.stevideter.com/2008/12/07/saveorupdate-versus-merge-in-hibernate/

Kita semua memiliki masalah yang jarang kita temui sehingga ketika kita melihatnya lagi, kita tahu kita telah menyelesaikan ini, tetapi tidak ingat bagaimana caranya.

NonUniqueObjectException dilemparkan ketika menggunakan Session.saveOrUpdate () di Hibernate adalah salah satu milik saya. Saya akan menambahkan fungsionalitas baru ke aplikasi yang kompleks. Semua tes unit saya berfungsi dengan baik. Kemudian dalam menguji UI, mencoba untuk menyimpan objek, saya mulai mendapatkan pengecualian dengan pesan "objek yang berbeda dengan nilai pengidentifikasi yang sama sudah dikaitkan dengan sesi." Berikut beberapa contoh kode dari Java Persistence with Hibernate.

            Session session = sessionFactory1.openSession();
            Transaction tx = session.beginTransaction();
            Item item = (Item) session.get(Item.class, new Long(1234));
            tx.commit();
            session.close(); // end of first session, item is detached

            item.getId(); // The database identity is "1234"
            item.setDescription("my new description");
            Session session2 = sessionFactory.openSession();
            Transaction tx2 = session2.beginTransaction();
            Item item2 = (Item) session2.get(Item.class, new Long(1234));
            session2.update(item); // Throws NonUniqueObjectException
            tx2.commit();
            session2.close();

Untuk memahami penyebab pengecualian ini, penting untuk memahami objek yang terlepas dan apa yang terjadi ketika Anda memanggil saveOrUpdate () (atau hanya memperbarui ()) pada objek yang terlepas.

Ketika kita menutup Sesi Hibernate individu, objek persisten yang sedang kita kerjakan terlepas. Ini berarti data masih dalam memori aplikasi, tetapi Hibernate tidak lagi bertanggung jawab untuk melacak perubahan pada objek.

Jika kemudian kita memodifikasi objek yang terpisah dan ingin memperbaruinya, kita harus memasang kembali objek tersebut. Selama proses pemasangan kembali, Hibernate akan memeriksa untuk melihat apakah ada salinan lain dari objek yang sama. Jika ada, itu harus memberitahu kami tidak tahu apa salinan "asli" itu lagi. Mungkin perubahan lain dibuat pada salinan lain yang kami harapkan akan disimpan, tetapi Hibernate tidak tahu tentang mereka, karena itu tidak mengelola mereka pada saat itu.

Daripada menyimpan data yang mungkin buruk, Hibernate memberi tahu kami tentang masalah melalui NonUniqueObjectException.

jadi, apa yang akan kita lakukan? Di Hibernate 3, kami memiliki gabungan () (di Hibernate 2, gunakan saveOrUpdateCopy ()). Metode ini akan memaksa Hibernate untuk menyalin perubahan apa pun dari instance terpisah ke instance yang ingin Anda simpan, dan dengan demikian menggabungkan semua perubahan dalam memori sebelum penyimpanan.

        Session session = sessionFactory1.openSession();
        Transaction tx = session.beginTransaction();
        Item item = (Item) session.get(Item.class, new Long(1234));
        tx.commit();
        session.close(); // end of first session, item is detached

        item.getId(); // The database identity is "1234"
        item.setDescription("my new description");
        Session session2 = sessionFactory.openSession();
        Transaction tx2 = session2.beginTransaction();
        Item item2 = (Item) session2.get(Item.class, new Long(1234));
        Item item3 = session2.merge(item); // Success!
        tx2.commit();
        session2.close();

Penting untuk dicatat bahwa gabungan mengembalikan referensi ke versi instance yang baru diperbarui. Itu bukan pemasangan kembali item ke Sesi. Jika Anda menguji misalnya kesetaraan (item == item3), Anda akan menemukan itu mengembalikan false dalam kasus ini. Anda mungkin ingin bekerja dengan item3 mulai saat ini.

Penting juga untuk dicatat bahwa Java Persistence API (JPA) tidak memiliki konsep objek yang dilepaskan dan disambungkan, dan menggunakan EntityManager.persist () dan EntityManager.merge ().

Saya telah menemukan secara umum bahwa ketika menggunakan Hibernate, saveOrUpdate () biasanya cukup untuk kebutuhan saya. Saya biasanya hanya perlu menggunakan gabungan ketika saya memiliki objek yang dapat memiliki referensi ke objek dari tipe yang sama. Baru-baru ini, penyebab pengecualian ada dalam kode yang memvalidasi bahwa referensi tidak rekursif. Saya memuat objek yang sama ke sesi saya sebagai bagian dari validasi, menyebabkan kesalahan.

Di mana Anda mengalami masalah ini? Apakah menggabungkan bekerja untuk Anda atau apakah Anda memerlukan solusi lain? Apakah Anda lebih suka untuk selalu menggunakan penggabungan, atau lebih suka menggunakannya hanya sesuai kebutuhan untuk kasus-kasus tertentu

HakunaMatata
sumber
Tautan ke artikel di webarchive, karena aslinya tidak tersedia: web.archive.org/web/20160521091122/http://www.stevideter.com:80/…
Eugen Labun
5

Sebenarnya perbedaan antara hibernate save()dan persist()metode tergantung pada kelas generator yang kita gunakan.

Jika kelas generator kami ditetapkan, maka tidak ada perbedaan antara save()dan persist() metode. Karena generator 'ditugaskan' berarti, sebagai seorang programmer kita perlu memberikan nilai kunci primer untuk disimpan dalam database dengan benar [Harap Anda tahu konsep generator ini] Dalam kasus selain dari kelas generator yang ditugaskan, misalkan jika nama kelas generator kita adalah Increment means hibernasi itu sendiri akan menetapkan nilai id kunci primer ke dalam database tepat [selain generator yang ditugaskan, hibernasi hanya digunakan untuk menjaga nilai id kunci primer diingat], jadi dalam hal ini jika kita memanggil save()atau persist()metode maka akan memasukkan catatan ke dalam database secara normal. Tapi dengar masalahnya, save()metode dapat mengembalikan nilai id kunci primer yang dihasilkan oleh hibernate dan kita dapat melihatnya dengan

long s = session.save(k);

Dalam kasus yang sama ini, persist()tidak akan pernah memberikan nilai apa pun kepada klien.

Hari Krishna
sumber
5

Saya menemukan contoh yang bagus yang menunjukkan perbedaan antara semua metode penyimpanan hibernate:

http://www.journaldev.com/3481/hibernate-session-merge-vs-update-save-saveorsupdate-persist-example

Secara singkat, menurut tautan di atas:

menyimpan()

  • Kami dapat meminta metode ini di luar transaksi. Jika kami menggunakan ini tanpa transaksi dan kami memiliki cascading antara entitas, maka hanya entitas utama yang akan disimpan kecuali kami menyiram sesi.
  • Jadi, jika ada objek lain yang dipetakan dari objek utama, mereka akan disimpan pada saat melakukan transaksi atau ketika kita menyapu sesi.

bertahan ()

  • Ini mirip dengan menggunakan save () dalam transaksi, jadi aman dan menangani semua benda yang mengalir.

saveOrUpdate ()

  • Dapat digunakan dengan atau tanpa transaksi, dan seperti halnya save (), jika digunakan tanpa transaksi, entitas yang dipetakan tidak akan disimpan, karena kami membilas sesi.

  • Hasil masuk atau memperbarui kueri berdasarkan data yang disediakan. Jika data ada di database, pembaruan permintaan dijalankan.

memperbarui()

  • Pembaruan hibernasi harus digunakan di mana kami tahu bahwa kami hanya memperbarui informasi entitas. Operasi ini menambahkan objek entitas ke konteks persisten dan perubahan lebih lanjut dilacak dan disimpan ketika transaksi dilakukan.
  • Oleh karena itu bahkan setelah memanggil pembaruan, jika kita menetapkan nilai apa pun dalam entitas, mereka akan diperbarui ketika transaksi dilakukan.

menggabungkan()

  • Penggabungan Hibernate dapat digunakan untuk memperbarui nilai yang ada, namun metode ini membuat salinan dari objek entitas yang lewat dan mengembalikannya. Objek yang dikembalikan adalah bagian dari konteks persisten dan dilacak untuk setiap perubahan, objek yang dikirimkan tidak dilacak. Ini adalah perbedaan utama dengan menggabungkan () dari semua metode lain.

Juga untuk contoh praktis dari semua ini, silakan merujuk ke tautan yang saya sebutkan di atas, itu menunjukkan contoh untuk semua metode yang berbeda ini.

OutOfMind
sumber
3

Seperti yang saya jelaskan dalam artikel ini , Anda harus lebih memilih metode JPA sebagian besar waktu, dan updateuntuk tugas-tugas pemrosesan batch.

Entitas JPA atau Hibernate dapat berada di salah satu dari empat negara berikut:

  • Transient (Baru)
  • Dikelola (Persisten)
  • Terpisah
  • Dihapus (Dihapus)

Transisi dari satu negara ke yang lain dilakukan melalui metode EntityManager atau Sesi.

Misalnya, JPA EntityManagermenyediakan metode transisi status entitas berikut.

masukkan deskripsi gambar di sini

Hibernate Sessionmengimplementasikan semua EntityManagermetode JPA dan menyediakan beberapa metode transisi status entitas tambahan seperti save, saveOrUpdatedan update.

masukkan deskripsi gambar di sini

Bertahan

Untuk mengubah status entitas dari Transient (Baru) ke Managed (Persisted), kita dapat menggunakan persistmetode yang ditawarkan oleh JPA EntityManageryang juga diwarisi oleh Hibernate Session.

The persistMetode memicu PersistEventyang ditangani oleh DefaultPersistEventListenerHibernate pendengar acara.

Karena itu, ketika menjalankan uji kasus berikut:

doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    LOGGER.info(
        "Persisting the Book entity with the id: {}", 
        book.getId()
    );
});

Hibernate menghasilkan pernyataan SQL berikut:

CALL NEXT VALUE FOR hibernate_sequence

-- Persisting the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

Perhatikan bahwa idditugaskan sebelum melampirkan Bookentitas ke Konteks Persistence saat ini. Ini diperlukan karena entitas yang dikelola disimpan dalam Mapstruktur di mana kunci dibentuk oleh jenis entitas dan pengenalnya dan nilainya adalah referensi entitas. Ini adalah alasan mengapa JPA EntityManagerdan Hibernate Sessiondikenal sebagai Cache Tingkat Pertama.

Saat memanggil persist, entitas hanya dilampirkan ke Konteks Persistence yang sedang berjalan, dan INSERT dapat ditunda hingga flushdipanggil.

Satu-satunya pengecualian adalah generator IDENTITY yang memicu INSERT segera karena itulah satu-satunya cara ia bisa mendapatkan pengenal entitas. Karena alasan ini, Hibernate tidak dapat memasukkan batch untuk entitas menggunakan generator IDENTITY. Untuk detail lebih lanjut tentang topik ini, lihat artikel ini .

Menyimpan

Khusus Hibernate saveMetode mendahului JPA dan sudah tersedia sejak awal proyek Hibernate.

The saveMetode memicu SaveOrUpdateEventyang ditangani oleh DefaultSaveOrUpdateEventListenerHibernate pendengar acara. Oleh karena itu, savemetode ini setara dengan updatedansaveOrUpdate metode .

Untuk melihat bagaimana savemetode ini bekerja, pertimbangkan uji kasus berikut:

doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);

    Long id = (Long) session.save(book);

    LOGGER.info(
        "Saving the Book entity with the id: {}", 
        id
    );
});

Saat menjalankan test case di atas, Hibernate menghasilkan pernyataan SQL berikut:

CALL NEXT VALUE FOR hibernate_sequence

-- Saving the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

Seperti yang Anda lihat, hasilnya identik dengan persistpemanggilan metode. Namun, tidak seperti persist,save metode mengembalikan pengenal entitas.

Untuk lebih jelasnya, lihat artikel ini .

Memperbarui

updateMetode khusus Hibernate dimaksudkan untuk mem-bypass mekanisme pemeriksaan kotor dan memaksa pembaruan entitas pada waktu flush.

The updateMetode memicu SaveOrUpdateEventyang ditangani oleh DefaultSaveOrUpdateEventListenerHibernate pendengar acara. Oleh karena itu, updatemetode ini setara dengan metode savedansaveOrUpdate .

Untuk melihat bagaimana updatemetode ini bekerja, perhatikan contoh berikut ini yang bertahan suatu Bookentitas dalam satu transaksi, kemudian memodifikasinya saat entitas dalam keadaan terlepas, dan itu memaksa SQL UPDATE menggunakan updatepemanggilan metode.

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

LOGGER.info("Modifying the Book entity");

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);

    LOGGER.info("Updating the Book entity");
});

Saat menjalankan uji kasus di atas, Hibernate menghasilkan pernyataan SQL berikut:

CALL NEXT VALUE FOR hibernate_sequence

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity
-- Updating the Book entity

UPDATE 
    book 
SET 
    author = 'Vlad Mihalcea', 
    isbn = '978-9730228236', 
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE 
    id = 1

Perhatikan bahwa UPDATEdieksekusi selama flush Konteks Persistence, tepat sebelum komit, dan itulah sebabnyaUpdating the Book entity pesan dicatat terlebih dahulu.

Menggunakan @SelectBeforeUpdate untuk menghindari pembaruan yang tidak perlu

Sekarang, UPDATE selalu akan dieksekusi bahkan jika entitas tidak berubah saat dalam keadaan terpisah. Untuk mencegah hal ini, Anda dapat menggunakan @SelectBeforeUpdateanotasi Hibernate yang akan memicu SELECTpernyataan yang diambilloaded state yang kemudian digunakan oleh mekanisme pemeriksaan kotor.

Jadi, jika kita membubuhi keterangan Bookentitas dengan @SelectBeforeUpdateanotasi:

@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {

    //Code omitted for brevity
}

Dan jalankan test case berikut ini:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);
});

Hibernate menjalankan pernyataan SQL berikut:

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

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

Perhatikan bahwa, kali ini, tidak ada UPDATE dieksekusi karena mekanisme pengecekan kotor Hibernate mendeteksi bahwa entitas tidak dimodifikasi.

SimpanOrUpdate

saveOrUpdateMetode khusus Hibernate hanyalah alias untuk savedan update.

The saveOrUpdateMetode memicu SaveOrUpdateEventyang ditangani oleh DefaultSaveOrUpdateEventListenerHibernate pendengar acara. Oleh karena itu, updatemetode ini setara dengan metode savedansaveOrUpdate .

Sekarang, Anda dapat menggunakan saveOrUpdatesaat Anda ingin bertahan suatu entitas atau memaksa UPDATEseperti yang diilustrasikan oleh 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");

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(_book);
});

Waspadalah terhadap NonUniqueObjectException

Salah satu masalah yang dapat terjadi dengan save,, updatedan saveOrUpdateadalah 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 NonUniqueObjectExceptionkarena yang kedua EntityManagersudah berisi Bookentitas 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)

Menggabungkan

Untuk menghindarinya NonUniqueObjectException, Anda perlu menggunakan mergemetode yang ditawarkan oleh JPA EntityManagerdan diwarisi oleh Hibernate Sessionjuga.

Seperti yang dijelaskan dalam artikel ini , mergemengambil snapshot entitas baru dari database jika tidak ada referensi entitas yang ditemukan dalam Konteks Persistence, dan salinan keadaan entitas terpisah dilewatkan ke mergemetode.

The mergeMetode memicu MergeEventyang ditangani oleh DefaultMergeEventListenerHibernate pendengar acara.

Untuk melihat bagaimana mergemetode ini bekerja, perhatikan contoh berikut ini yang bertahan suatu Bookentitas dalam satu transaksi, kemudian memodifikasinya saat entitas berada dalam keadaan terlepas, dan meneruskan entitas yang terlepas ke mergedalam Konteks Persistence selanjutnya.

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

LOGGER.info("Modifying the Book entity");

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

doInJPA(entityManager -> {
    Book book = entityManager.merge(_book);

    LOGGER.info("Merging the Book entity");

    assertFalse(book == _book);
});

Saat menjalankan test case di atas, Hibernate menjalankan pernyataan SQL berikut:

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity

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

Perhatikan bahwa referensi entitas yang dikembalikan oleh mergeberbeda dengan yang kita lewati untuk mergemetode ini.

Sekarang, meskipun Anda sebaiknya memilih menggunakan JPA mergesaat menyalin status entitas terpisah, ekstra SELECTbisa bermasalah saat menjalankan tugas pemrosesan batch.

Untuk alasan ini, Anda sebaiknya menggunakan update ketika Anda yakin bahwa tidak ada referensi entitas yang sudah dilampirkan ke Konteks Persistence yang sedang berjalan dan bahwa entitas yang terlepas telah dimodifikasi.

Untuk detail lebih lanjut tentang topik ini, lihat artikel ini .

Kesimpulan

Untuk bertahan suatu entitas, Anda harus menggunakan persistmetode JPA . Untuk menyalin status entitas terpisah, mergeharus lebih disukai. The updateMetode ini berguna untuk tugas-tugas pemrosesan batch saja. The savedan saveOrUpdatehanya alias untuk updatedan Anda tidak boleh menggunakannya sama sekali.

Beberapa pengembang memanggil savebahkan ketika entitas sudah dikelola, tetapi ini adalah kesalahan dan memicu peristiwa yang berlebihan karena, untuk entitas yang dikelola, UPDATE secara otomatis ditangani pada waktu flush konteks Persistence.

Untuk lebih jelasnya, lihat artikel ini .

Vlad Mihalcea
sumber
2

Ketahuilah bahwa jika Anda memanggil pembaruan pada objek yang terpisah, akan selalu ada pembaruan yang dilakukan dalam database apakah Anda mengubah objek atau tidak. Jika bukan itu yang Anda inginkan, Anda harus menggunakan Session.lock () dengan LockMode.None.

Anda harus memanggil pembaruan hanya jika objek diubah di luar ruang lingkup sesi Anda saat ini (ketika dalam mode terpisah).

bernardn
sumber
1

Tidak satu pun dari jawaban berikut yang benar. Semua metode ini tampaknya sama, tetapi dalam praktiknya melakukan hal-hal yang benar-benar berbeda. Sulit memberikan komentar pendek. Lebih baik memberikan tautan ke dokumentasi lengkap tentang metode ini: http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/objectstate.html

Anton Popovich
sumber
11
Tolong beri tahu kami mengapa jawaban berikut tidak benar.
Erran Morad
0

Tidak ada jawaban di atas yang lengkap. Meskipun jawaban Leo Theobald terlihat sebagai jawaban terdekat.

Poin dasarnya adalah bagaimana hibernate berhadapan dengan status entitas dan bagaimana ia menangani mereka ketika ada perubahan status. Semuanya harus dilihat sehubungan dengan flushes dan commit juga, yang tampaknya semua orang abaikan sama sekali.

JANGAN PERNAH MENGGUNAKAN METODE SIMPAN HIBERNATE. LUPAKAN BAHWA ITU ADA DI HIBERNATE!

Bertahan

Seperti yang semua orang jelaskan, Persist pada dasarnya mentransisikan suatu entitas dari kondisi "Transient" ke "Managed". Pada titik ini, slush atau commit dapat membuat pernyataan insert. Tetapi entitas masih akan tetap dalam status "Dikelola". Itu tidak berubah dengan flush.

Pada titik ini, jika Anda "bertahan" lagi tidak akan ada perubahan. Dan tidak akan ada lagi penyelamatan jika kita mencoba untuk tetap bertahan.

Kegembiraan dimulai ketika kami mencoba mengusir entitas.

Penggusuran adalah fungsi khusus Hibernate yang akan mentransisikan entitas dari "Managed" ke "Detached". Kami tidak dapat memanggil bertahan pada entitas yang terpisah. Jika kita melakukan itu, maka Hibernate memunculkan pengecualian dan seluruh transaksi akan dibatalkan saat komit.

Gabungkan vs Pembaruan

Ini adalah 2 fungsi menarik yang melakukan hal yang berbeda ketika ditangani dengan cara yang berbeda. Keduanya mencoba mentransisikan entitas dari keadaan "Terpisah" ke keadaan "Terkelola". Tetapi melakukannya secara berbeda.

Memahami fakta bahwa Terpisah berarti semacam keadaan "offline". dan dikelola berarti keadaan "Online".

Perhatikan kode di bawah ini:

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.merge(entity);

    ses1.delete(entity);

    tx1.commit();

Kapan kamu melakukan ini? Apa yang kamu pikir akan terjadi? Jika Anda mengatakan ini akan menimbulkan pengecualian, maka Anda benar. Ini akan memunculkan eksepsi karena, penggabungan telah bekerja pada objek entitas, yang merupakan keadaan terpisah. Tapi itu tidak mengubah keadaan objek.

Di belakang layar, gabungan akan menaikkan kueri pemilihan dan pada dasarnya mengembalikan salinan entitas yang dalam keadaan terlampir. Perhatikan kode di bawah ini:

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();
    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    HibEntity copied = (HibEntity)ses1.merge(entity);
    ses1.delete(copied);

    tx1.commit();

Sampel di atas berfungsi karena penggabungan telah membawa entitas baru ke dalam konteks yang dalam kondisi tetap.

Ketika diterapkan dengan Perbarui yang sama berfungsi dengan baik karena pembaruan tidak benar-benar membawa salinan entitas seperti penggabungan.

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.update(entity);

    ses1.delete(entity);

    tx1.commit();

Pada saat yang sama dalam pelacakan debug kita dapat melihat bahwa Pembaruan belum meningkatkan kueri SQL dari pilih seperti penggabungan.

menghapus

Dalam contoh di atas saya menggunakan delete tanpa berbicara tentang delete. Hapus pada dasarnya akan mentransisikan entitas dari status terkelola ke status "dihapus". Dan ketika flushed atau commit akan mengeluarkan perintah delete untuk menyimpan.

Namun dimungkinkan untuk membawa entitas kembali ke keadaan "dikelola" dari keadaan "dihapus" menggunakan metode bertahan.

Semoga penjelasan di atas mengklarifikasi keraguan.

Bir jahe
sumber