Apa cara yang tepat untuk melampirkan kembali objek yang terlepas di Hibernate?

186

Saya memiliki situasi di mana saya harus melampirkan kembali objek yang terpisah ke sesi hibernasi, meskipun objek dengan identitas yang sama DAPAT ada di sesi, yang akan menyebabkan kesalahan.

Saat ini, saya dapat melakukan satu dari dua hal.

  1. getHibernateTemplate().update( obj ) Ini berfungsi jika dan hanya jika suatu objek belum ada di sesi hibernasi. Pengecualian dilemparkan yang menyatakan objek dengan pengenal yang diberikan sudah ada di sesi ketika saya membutuhkannya nanti.

  2. getHibernateTemplate().merge( obj ) Ini berfungsi jika dan hanya jika ada objek di sesi hibernasi. Pengecualian dilemparkan ketika saya membutuhkan objek dalam sesi nanti jika saya menggunakan ini.

Dengan dua skenario ini, bagaimana saya bisa secara umum melampirkan sesi ke objek? Saya tidak ingin menggunakan pengecualian untuk mengontrol aliran solusi masalah ini, karena harus ada solusi yang lebih elegan ...

Stefan Kendall
sumber

Jawaban:

181

Jadi sepertinya tidak ada cara untuk memasang kembali entitas basi terlepas di JPA.

merge() akan mendorong status basi ke DB, dan menimpa pembaruan yang mengintervensi.

refresh() tidak dapat dipanggil pada entitas yang terpisah.

lock() tidak dapat dipanggil pada entitas yang terpisah, dan bahkan jika itu bisa, dan itu memang menempel kembali entitas, memanggil 'kunci' dengan argumen 'LockMode.NONE' menyiratkan bahwa Anda sedang mengunci, tetapi tidak mengunci, adalah bagian yang paling berlawanan dari desain API Saya tidak pernah melihat.

Jadi kamu terjebak. Ada sebuah detach()metode, tetapi tidak ada attach()atau tidak reattach(). Langkah jelas dalam siklus hidup objek tidak tersedia untuk Anda.

Menilai dari sejumlah pertanyaan serupa tentang JPA, tampaknya bahkan jika JPA mengklaim memiliki model yang koheren, itu tentu saja tidak cocok dengan model mental kebanyakan programmer, yang telah dikutuk untuk membuang waktu berjam-jam mencoba memahami bagaimana cara mendapatkan JPA untuk melakukan hal-hal yang paling sederhana, dan berakhir dengan kode manajemen cache di seluruh aplikasi mereka.

Tampaknya satu-satunya cara untuk melakukannya adalah membuang entitas terpisah basi Anda dan lakukan permintaan pencarian dengan id yang sama, yang akan mengenai L2 atau DB.

Mik

mikhailfranco
sumber
1
Saya ingin tahu apakah ada alasan spesifikasi JPA tidak memungkinkan refresh()pada entitas yang terpisah? Melihat melalui spesifikasi 2.0, saya tidak melihat adanya pembenaran; Hanya saja itu tidak diizinkan.
FGreg
11
Ini jelas TIDAK akurat. Dari JPwH: *Reattaching a modified detached instance* A detached instance may be reattached to a new Session (and managed by this new persistence context) by calling update() on the detached object. In our experience, it may be easier for you to understand the following code if you rename the update() method in your mind to reattach()—however, there is a good reason it’s called updating.Lebih banyak dapat ditemukan di bagian 9.3.2
cwash
Benda persisten bekerja dengan sangat baik, bendera kotor diatur berdasarkan delta antara muatan awal dan nilai pada waktu flush (). Objek yang terpisah perlu, dan saat ini tidak memiliki fungsi ini. Cara hibernate untuk melakukannya adalah dengan menambahkan hash / id tambahan untuk objek yang terpisah. Dan simpan snapshot keadaan terakhir dari objek yang terpisah, seperti yang mereka lakukan untuk objek yang persisten. Jadi mereka dapat memanfaatkan semua kode yang ada dan membuatnya berfungsi untuk objek yang terpisah. Dengan cara ini seperti yang dicatat oleh @mikhailfranco, kami tidak akan "mendorong status basi ke DB, dan menimpa pembaruan yang mengintervensi"
tom
2
Menurut javadoc Hibernate (tapi bukan JPA), lock(LockMode.NONE)sebenarnya bisa dipanggil pada objek sementara, dan itu menempel kembali entitas ke sesi. Lihat stackoverflow.com/a/3683370/14379
seanf
kunci tidak bekerja untuk saya: java.lang.IllegalArgumentException: entitas tidak dalam konteks kegigihan di org.hibernate.internal.SessionImpl.lock (SessionImpl.java:3491) di org.hibernate.internal.SessionImpl.lock (SessionImpl. java: 3482) di com.github.vok.framework.DisableTransactionControlEMDelegate.lock (DB.kt)
Martin Vysny
32

Semua jawaban ini kehilangan perbedaan penting. pembaruan () digunakan untuk (kembali) melampirkan grafik objek Anda ke Sesi. Objek yang Anda lewati adalah objek yang dikelola.

menggabungkan () sebenarnya bukan API lampiran (kembali). Perhatikan penggabungan () memiliki nilai pengembalian? Itu karena ia mengembalikan Anda grafik yang dikelola, yang mungkin bukan grafik yang Anda lewati. gabungan () adalah API JPA dan perilakunya diatur oleh spesifikasi JPA. Jika objek yang Anda lewati untuk menggabungkan () sudah dikelola (sudah dikaitkan dengan Sesi) maka itulah grafik yang berfungsi dengan Hibernate; objek yang diteruskan adalah objek yang sama yang dikembalikan dari gabungan (). Namun, jika objek yang Anda gabungkan () dilepaskan, Hibernate membuat grafik objek baru yang dikelola dan menyalin keadaan dari grafik Anda yang terlepas ke grafik terkelola yang baru. Sekali lagi, ini semua ditentukan dan diatur oleh spesifikasi JPA.

Dalam hal strategi generik untuk "memastikan entitas ini dikelola, atau membuatnya dikelola", itu tergantung pada apakah Anda ingin memperhitungkan data yang belum dimasukkan juga. Dengan asumsi Anda melakukannya, gunakan sesuatu seperti

if ( session.contains( myEntity ) ) {
    // nothing to do... myEntity is already associated with the session
}
else {
    session.saveOrUpdate( myEntity );
}

Perhatikan saya menggunakan saveOrUpdate () daripada update (). Jika Anda tidak ingin data yang belum dimasukkan ditangani di sini, gunakan pembaruan () sebagai gantinya ...

Steve Ebersole
sumber
3
Ini adalah jawaban yang tepat untuk pertanyaan ini - kasus ditutup!
cwash
2
Session.contains(Object)memeriksa dengan referensi. Jika sudah ada Entitas lain yang mewakili baris yang sama dalam sesi dan Anda melewati instance terpisah Anda akan mendapatkan pengecualian.
djmj
Sebagai Session.contains(Object)pemeriksaan dengan referensi, jika ada Entitas lain yang mewakili baris yang sama dalam sesi, itu akan mengembalikan false, dan itu akan memperbaruinya.
AxelWass
19

Jawaban tidak jujur: Anda mungkin mencari konteks ketekunan yang diperpanjang. Ini adalah salah satu alasan utama di balik Seam Framework ... Jika Anda kesulitan untuk menggunakan Hibernate di Spring pada khususnya, lihat bagian ini dari dokumen Seam ini.

Jawaban diplomatik: Ini dijelaskan dalam dokumen Hibernate . Jika Anda membutuhkan klarifikasi lebih lanjut, lihat Bagian 9.3.2 Java Persistence dengan Hibernate yang disebut "Bekerja dengan Objek yang Dilepaskan." Saya sangat menyarankan Anda mendapatkan buku ini jika Anda melakukan lebih dari CRUD dengan Hibernate.

cwash
sumber
5
Dari seamframework.org : "Pengembangan aktif Seam 3 telah dihentikan oleh Red Hat." Tautan "potongan dokumen Seam ini" juga mati.
badbishop
14

Jika Anda yakin entitas Anda belum dimodifikasi (atau jika Anda setuju modifikasi apa pun akan hilang), maka Anda dapat memasangnya kembali ke sesi dengan kunci.

session.lock(entity, LockMode.NONE);

Ini tidak akan mengunci apa pun, tetapi akan mendapatkan entitas dari cache sesi atau (jika tidak ditemukan di sana) membacanya dari DB.

Ini sangat berguna untuk mencegah LazyInitException ketika Anda menavigasi hubungan dari entitas "lama" (dari HttpSession misalnya). Anda pertama kali "melampirkan kembali" entitas.

Menggunakan get juga berfungsi, kecuali ketika Anda mendapatkan pemetaan warisan (yang sudah akan melempar pengecualian pada getId ()).

entity = session.get(entity.getClass(), entity.getId());
John Rizzo
sumber
2
Saya ingin mengaitkan kembali entitas dengan sesi. Sayangnya, Session.lock(entity, LockMode.NONE)gagal dengan pengecualian yang mengatakan: tidak dapat mengaitkan kembali koleksi transien yang tidak diinisialisasi. Bagaimana cara mengatasinya?
dma_k
1
Sebenarnya saya tidak sepenuhnya benar. Menggunakan kunci () menempel kembali entitas Anda, tetapi tidak entitas lain terikat padanya. Jadi, jika Anda melakukan entitas.getOtherEntity (). GetYetAnotherEntity (), Anda mungkin memiliki pengecualian LazyInit. Satu-satunya cara saya tahu untuk mengatasinya adalah menggunakan find. entity = em.find (entity.getClass (), entity.getId ();
John Rizzo
Tidak ada Session.find()metode API. Mungkin maksud Anda Session.load(Object object, Serializable id).
dma_k
11

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 Sessionflush 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 EntityManagerantarmuka.

Untuk memahami transisi status entitas JPA dengan lebih baik, pertimbangkan diagram berikut:

Transisi status entitas JPA

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:

Transisi status entitas hibernasi

Menggabungkan entitas yang terpisah

Penggabungan akan menyalin status entitas terpisah (sumber) ke instance entitas terkelola (tujuan).

Anggaplah kita telah mempertahankan Bookentitas berikut , dan sekarang entitas terlepas seperti EntityManageryang 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 mergemetode:

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 Bookentitas dan bahwa kami memodifikasinya ketika Bookentitas 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 updatemetode mengharuskan Anda untuk unwrapyang EntityManagerke 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 @SelectBeforeUpdateanotasi 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 updateadalah 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)

Kesimpulan

The mergemetode 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 updatebagus untuk pembaruan batch karena dapat mencegah pernyataan SELECT tambahan yang dihasilkan oleh mergeoperasi, sehingga mengurangi waktu eksekusi pembaruan batch.

Vlad Mihalcea
sumber
Jawaban bagus. Saya bertanya-tanya tentang @SelectBeforeUpdateanotasi. Kapan pilih dipicu? Saat menelepon update, tepat sebelum memerah atau tidak terlalu penting (bisa jadi masalah jika hibernate mengambil semua entitas yang beranotasi dalam satu panggilan sebelum memerah)?
Andronicus
The @SelectBeforeUpdatememicu SELECT selama Konteks Kegigihan flushoperasi. Periksa dengan getDatabaseSnapshotmetode dalamDefaultFlushEntityEventListener untuk lebih jelasnya.
Vlad Mihalcea
10

Saya kembali ke JavaDoc untuk org.hibernate.Sessiondan menemukan yang berikut:

Mesin virtual sementara dapat dibuat gigih dengan menelepon save(), persist()atau saveOrUpdate(). Contoh yang persisten dapat dibuat sementara dengan menelepon delete(). Setiap instance yang dikembalikan oleh get()atau load()metode bersifat persisten. Contoh terpisah dapat dilakukan terus-menerus dengan menelepon update(), saveOrUpdate(), lock()atau replicate(). Keadaan instance sementara atau terpisah juga dapat dibuat gigih sebagai instance persisten baru dengan menelepon merge().

Dengan demikian update(), saveOrUpdate(), lock(), replicate()dan merge()adalah pilihan kandidat.

update(): Akan melempar pengecualian jika ada instance persisten dengan pengidentifikasi yang sama.

saveOrUpdate(): Baik menyimpan atau memperbarui

lock(): Usang

replicate(): Tetap kondisi instance terpisah yang diberikan, menggunakan kembali nilai pengidentifikasi saat ini.

merge(): Mengembalikan objek persisten dengan pengidentifikasi yang sama. Contoh yang diberikan tidak menjadi terkait dengan sesi.

Oleh karena itu, lock()tidak boleh digunakan secara langsung dan berdasarkan persyaratan fungsional satu atau lebih dari mereka dapat dipilih.

Amitabha Roy
sumber
7

Saya melakukannya seperti itu di C # dengan NHibernate, tetapi harus bekerja dengan cara yang sama di Jawa:

public virtual void Attach()
{
    if (!HibernateSessionManager.Instance.GetSession().Contains(this))
    {
        ISession session = HibernateSessionManager.Instance.GetSession();
        using (ITransaction t = session.BeginTransaction())
        {
            session.Lock(this, NHibernate.LockMode.None);
            t.Commit();
        }
    }
}

Kunci Pertama dipanggil pada setiap objek karena Berisi selalu salah. Masalahnya adalah NHibernate membandingkan objek berdasarkan id dan jenis basis data. Berisi menggunakan equalsmetode, yang membandingkan dengan referensi jika tidak ditimpa. Dengan equalsmetode itu berfungsi tanpa Pengecualian:

public override bool Equals(object obj)
{
    if (this == obj) { 
        return true;
    } 
    if (GetType() != obj.GetType()) {
        return false;
    }
    if (Id != ((BaseObject)obj).Id)
    {
        return false;
    }
    return true;
}
Verena Haunschmid
sumber
4

Session.contains(Object obj) memeriksa referensi dan tidak akan mendeteksi instance berbeda yang mewakili baris yang sama dan sudah terlampir padanya.

Di sini solusi generik saya untuk Entitas dengan properti pengidentifikasi.

public static void update(final Session session, final Object entity)
{
    // if the given instance is in session, nothing to do
    if (session.contains(entity))
        return;

    // check if there is already a different attached instance representing the same row
    final ClassMetadata classMetadata = session.getSessionFactory().getClassMetadata(entity.getClass());
    final Serializable identifier = classMetadata.getIdentifier(entity, (SessionImplementor) session);

    final Object sessionEntity = session.load(entity.getClass(), identifier);
    // override changes, last call to update wins
    if (sessionEntity != null)
        session.evict(sessionEntity);
    session.update(entity);
}

Ini adalah salah satu dari beberapa aspek .Net EntityFramework yang saya suka, opsi melampirkan yang berbeda mengenai entitas yang berubah dan properti mereka.

djmj
sumber
3

Saya datang dengan solusi untuk "menyegarkan" objek dari toko kegigihan yang akan menjelaskan objek lain yang mungkin sudah dilampirkan pada sesi:

public void refreshDetached(T entity, Long id)
{
    // Check for any OTHER instances already attached to the session since
    // refresh will not work if there are any.
    T attached = (T) session.load(getPersistentClass(), id);
    if (attached != entity)
    {
        session.evict(attached);
        session.lock(entity, LockMode.NONE);
    }
    session.refresh(entity);
}
WhoopP
sumber
2

Maaf, sepertinya tidak dapat menambahkan komentar (belum?).

Menggunakan Hibernate 3.5.0-Final

Sementara Session#lockmetode ini sudah usang, javadoc tidak menyarankan menggunakan Session#buildLockRequest(LockOptions)#lock(entity)dan jika Anda memastikan asosiasi Anda miliki cascade=lock, pemuatan malas juga tidak menjadi masalah.

Jadi, metode lampirkan saya agak mirip

MyEntity attach(MyEntity entity) {
    if(getSession().contains(entity)) return entity;
    getSession().buildLockRequest(LockOptions.NONE).lock(entity);
    return entity;

Tes awal menunjukkan itu manjur.

Gwaptiva
sumber
2

Mungkin berperilaku sedikit berbeda di Eclipselink. Untuk melampirkan kembali objek yang terlepas tanpa mendapatkan data basi, biasanya saya lakukan:

Object obj = em.find(obj.getClass(), id);

dan sebagai langkah kedua opsional (untuk membuat cache tidak valid):

em.refresh(obj)
Hartmut P.
sumber
1

coba getHibernateTemplate (). replikasi (entitas, ReplicationMode.LATEST_VERSION)

Pavitar Singh
sumber
1

Dalam posting asli, ada dua metode, update(obj)dan merge(obj)yang disebutkan berfungsi, tetapi dalam situasi yang berlawanan. Jika ini benar, maka mengapa tidak menguji untuk melihat apakah objek sudah ada di sesi pertama, lalu panggil update(obj)jika itu, jika tidak panggilmerge(obj) .

Tes untuk keberadaan dalam sesi ini adalah session.contains(obj). Oleh karena itu, saya akan berpikir kode pseudo berikut ini akan berfungsi:

if (session.contains(obj))
{
    session.update(obj);
}
else 
{
    session.merge(obj);
}
John DeRegnaucourt
sumber
2
berisi () pemeriksaan membandingkan dengan referensi, tetapi fungsi hibernasi bekerja dengan basis data id. session.merge tidak akan pernah dipanggil dalam kode Anda.
Verena Haunschmid
1

untuk memasang kembali objek ini, Anda harus menggunakan gabungan ();

Metode ini menerima parameter entitas Anda terlepas dan mengembalikan entitas akan dilampirkan dan dimuat kembali dari Database.

Example :
    Lot objAttach = em.merge(oldObjDetached);
    objAttach.setEtat(...);
    em.persist(objAttach);
Ryuku
sumber
0

memanggil penggabungan pertama () (untuk memperbarui instance persisten), lalu mengunci (LockMode.NONE) (untuk melampirkan instance saat ini, bukan yang dikembalikan oleh penggabungan ()) tampaknya berfungsi untuk beberapa kasus penggunaan.

cheesus
sumber
0

Properti hibernate.allow_refresh_detached_entity melakukan trik untuk saya. Tetapi ini adalah aturan umum, jadi tidak cocok jika Anda ingin melakukannya hanya dalam beberapa kasus. Saya harap ini membantu.

Diuji pada Hibernate 5.4.9

SessionFactoryOptionsBuilder

Radeck
sumber
-6
try getHibernateTemplate().saveOrUpdate()
Ben Hammond
sumber