Hibernasi - Pembaruan batch menghasilkan jumlah baris yang tidak terduga dari pembaruan: 0 jumlah baris aktual: 0 diharapkan: 1

147

Saya mendapatkan kesalahan hibernasi berikut. Saya dapat mengidentifikasi fungsi yang menyebabkan masalah. Sayangnya ada beberapa panggilan DB dalam fungsi tersebut. Saya tidak dapat menemukan baris yang menyebabkan masalah sejak hibernasi membilas sesi di akhir transaksi. Kesalahan hibernasi yang disebutkan di bawah ini tampak seperti kesalahan umum. Bahkan tidak disebutkan Bean mana yang menyebabkan masalah. Adakah yang akrab dengan kesalahan hibernasi ini?

org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1
        at org.hibernate.jdbc.BatchingBatcher.checkRowCount(BatchingBatcher.java:93)
        at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:79)
        at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:195)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:142)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
        at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:584)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransacti
onManager.java:500)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManag
er.java:473)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(Transaction
AspectSupport.java:267)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)
Sujee
sumber
Terima kasih @Peter Mortensen. Saya telah memperbarui email saya.
Sujee
Saya memiliki masalah yang sama. Ini bukan masalah besar karena sangat jarang terjadi. Menggunakan show_sql tidak praktis karena mereproduksi perilaku ini membutuhkan jutaan transaksi. Namun, karena ini terjadi berulang kali selama pengujian sistem yang saya jalankan (yang memiliki triliunan transaksi), saya curiga ada alasan khusus.
daniel_or_else
Saya mengalami masalah ini saat mencoba memperbarui baris yang TIDAK memiliki perubahan. Jangan perbarui sesuatu jika tidak ada perbedaan.
fifman

Jawaban:

65

Tanpa kode dan pemetaan untuk transaksi Anda, hampir tidak mungkin untuk menyelidiki masalahnya.

Namun, untuk mendapatkan penanganan yang lebih baik tentang penyebab masalah, coba yang berikut ini:

  • Dalam konfigurasi hibernasi Anda, setel hibernate.show_sql ke true. Ini akan menunjukkan kepada Anda SQL yang dijalankan dan menyebabkan masalah.
  • Setel level log untuk Spring dan Hibernate ke DEBUG, sekali lagi ini akan memberi Anda gambaran yang lebih baik tentang jalur mana yang menyebabkan masalah.
  • Buat pengujian unit yang mereplikasi masalah tanpa mengonfigurasi manajer transaksi di Spring. Ini akan memberi Anda gagasan yang lebih baik tentang baris kode yang menyinggung.

Semoga membantu.

beny23
sumber
22
hibernate.show_sql> saya lebih suka menyarankan untuk menyetel tingkat kategori log org.hibernate.SQL ke DEBUG. Dengan cara ini Anda tidak perlu mengubah konfigurasi hibernasi hanya untuk logging.
Thierry
Penggunaan "show_sql" bisa sangat bertele-tele dan tidak praktis dalam produksi. Untuk ini saya telah memodifikasi klausa catch pada baris 74 dari kelas BatchingBatcher untuk mencetak pernyataan dengan ps.toString () agar hanya memiliki pernyataan yang bermasalah.
Orden
76

Saya mendapat pengecualian yang sama saat menghapus catatan dengan Id yang tidak ada sama sekali. Jadi periksa bahwa catatan yang Anda perbarui / Hapus benar-benar ada di DB

shreyas
sumber
13
Saya mengalami masalah ini ketika saya menghapus anak dari hubungan orang tua-anak, menyimpan orang tua (yang menghapus anak) dan kemudian mencoba juga menghapus anak secara manual.
Dave Thieben
Ini memecahkan masalah saya. Catatan tidak ada dan layanan saya memanggil metode updateAll () sementara itu sebenarnya diperlukan untuk memanggil metode createOrUpdateAll (). Terima kasih.
Mital Pritmani
3
jadi bagaimana mengatasi masalah tersebut? Jika saya mendapatkan catatan, lalu menghapusnya; tetapi jika sistem sudah menghapusnya sebelum saya menghapusnya, yang lainnya, aplikasi saya akan membuang pengecualian.
Stony
@davethieben, inilah informasi yang saya butuhkan. Saya tidak menyadari bahwa hubungan orang tua-anak tetap ada saat penghapusan.
turun salju
Solusinya adalah dengan menggunakan pilih untuk pembaruan saat mengambil catatan untuk mengunci baris, jadi tidak ada lagi yang bisa menghapusnya sebelum Anda melakukannya.
Matius Baca
56

Solusi: Dalam file pemetaan Hibernasi untuk properti id, jika Anda menggunakan kelas generator apa pun, untuk properti itu Anda tidak boleh menyetel nilai secara eksplisit dengan menggunakan metode penyetel.

Jika Anda menyetel nilai properti Id secara eksplisit, ini akan menyebabkan kesalahan di atas. Periksa ini untuk menghindari kesalahan ini. atau Kesalahannya muncul ketika Anda menyebutkan dalam file pemetaan generator bidang = "native" atau "incremental" dan di DATABASE Anda tabel yang dipetakan bukan auto_incremented Solusi: Buka DATABASE Anda dan perbarui tabel Anda untuk menyetel auto_increment

Rēda Biramanē
sumber
2
Benar-benar tepat. Ini pasti jawaban yang benar. Terima kasih @ Rēda Biramanē
Kuldeep Verma
1
Namun , Anda BISA mengatur nilai ID ke '0', dan kemudian Hibernate akan merasa bebas untuk menimpanya
forresthopkinsa
1
Terima kasih! Tidak yakin mengapa ini bukan jawaban dengan peringkat tertinggi.
Stuart McIntyre
19

Ini terjadi pada saya sekali secara tidak sengaja ketika saya menetapkan ID tertentu ke beberapa objek (pengujian) dan kemudian saya mencoba menyimpannya di database. Masalahnya adalah bahwa dalam database terdapat kebijakan khusus untuk menyiapkan ID objek. Hanya saja, jangan berikan ID jika Anda memiliki kebijakan di tingkat Hibernasi.

cSn
sumber
16

Dalam kasus saya, saya sampai pada pengecualian ini dalam dua kasus serupa:

  • Dalam metode yang dijelaskan dengan @Transactionalsaya mendapat panggilan ke layanan lain (dengan waktu respons yang lama). Metode memperbarui beberapa properti entitas (setelah metode, entitas masih ada di database). Jika pengguna meminta metode dua kali (karena menurutnya itu tidak berfungsi pertama kali) saat keluar dari metode transaksional untuk kedua kalinya, Hibernate mencoba memperbarui entitas yang sudah mengubah statusnya dari awal transaksi. Saat Hibernate mencari entitas dalam status, dan menemukan entitas yang sama tetapi sudah diubah oleh permintaan pertama, itu membuat pengecualian karena tidak dapat memperbarui entitas. Ini seperti konflik di GIT.
  • Saya memiliki permintaan otomatis (untuk memantau platform) yang memperbarui entitas (dan rollback manual beberapa detik kemudian). Namun platform ini sudah digunakan oleh tim penguji. Ketika penguji melakukan pengujian di entitas yang sama dengan permintaan otomatis, (dalam seperseratus milidetik yang sama), saya mendapatkan pengecualian. Seperti dalam kasus sebelumnya, saat keluar dari transaksi kedua, entitas yang diambil sebelumnya sudah berubah.

Kesimpulan: dalam kasus saya, itu bukanlah masalah yang dapat ditemukan di dalam kode. Pengecualian ini dilemparkan ketika Hibernate menemukan bahwa entitas yang pertama kali diambil dari database berubah selama transaksi saat ini , sehingga tidak dapat memindahkannya ke database karena Hibernate tidak tahu versi mana yang benar dari entitas: yang saat ini pengambilan transaksi di awal; atau yang sudah tersimpan di database.

Solusi: untuk mengatasi masalah ini, Anda harus bermain-main dengan Hibernate LockMode untuk menemukan yang paling sesuai dengan kebutuhan Anda.

Sergio Lema
sumber
Hai, terima kasih atas jawaban Anda yang membantu. Bisakah Anda memberi tahu, LockMode mana yang Anda gunakan pada akhirnya, untuk menghilangkan kesalahan. Saya menghadapi masalah serupa, ketika API dipukul secara berturut-turut dalam milidetik yang berbeda, karena pengguna mengklik dua kali secara tidak sengaja. Penguncian Pesimistis merusak kinerja; jadi akan tertarik untuk mengetahui apa yang akhirnya Anda cari?
Madhur Bhaiya
1
Sudah lama sekali saya tidak memiliki masalah dan saya tidak ingat dengan baik solusi yang tepat yang telah saya berikan, tetapi itu LockMode.READatau LockMode.OPTIMISTIC.
Sergio Lema
13

Saya baru saja mengalami masalah ini dan menemukan saya sedang menghapus catatan dan mencoba memperbaruinya setelah itu dalam transaksi Hibernate.

Julius
sumber
9

Ini bisa terjadi saat pemicu mengeksekusi kueri DML (modifikasi data) tambahan yang memengaruhi jumlah baris. Solusi saya adalah menambahkan yang berikut ini di bagian atas pemicu saya:

SET NOCOUNT ON;
Mr TA
sumber
1
Jawaban ini mengirim saya ke arah yang benar. Saya bekerja dengan database lama yang memiliki pemicu di atasnya - untungnya saya mengganti apa yang dilakukan pemicu itu dengan kode, jadi saya bisa menghapusnya.
S. Baggy
2
+1 Itu juga masalah saya. Saya menggunakan postgresql, jadi perlu menggunakan anotasi @SQLInsert untuk menonaktifkan pemeriksaan jumlah baris: technology-ebay.de/the-teams/mobile-de/blog/…
s1mm0t
SET NOCOUNT OFF *
G. Ciardini
7

Saya menghadapi masalah yang sama. Kode berfungsi di lingkungan pengujian. Tapi itu tidak bekerja di lingkungan pementasan.

org.hibernate.jdbc.BatchedTooManyRowsAffectedException: Batch update returned unexpected row count from update [0]; actual row count: 3; expected: 1

Masalahnya adalah tabel tersebut memiliki entri tunggal untuk setiap kunci utama dalam menguji tabel DB. Tetapi dalam pementasan DB ada beberapa entri untuk kunci utama yang sama. (Masalahnya adalah dalam pementasan DB, tabel tidak memiliki batasan kunci utama juga ada banyak entri.)

Jadi setiap kali operasi pembaruan itu gagal. Ia mencoba untuk memperbarui catatan tunggal dan mengharapkan untuk mendapatkan jumlah pembaruan sebagai 1. Tetapi karena ada 3 catatan dalam tabel untuk kunci utama yang sama, Hasil pembaruan menghitung menemukan 3. Karena jumlah pembaruan yang diharapkan dan jumlah pembaruan hasil aktual tidak cocok , Ini melempar pengecualian dan memutar kembali.

Setelah saya menghapus semua catatan yang memiliki kunci utama duplikat dan menambahkan kendala kunci utama. Ini bekerja dengan baik.

Hibernate - Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1

jumlah baris aktual: 0 // berarti tidak ada catatan yang ditemukan untuk memperbarui
pembaruan: 0 // berarti tidak ada catatan yang ditemukan sehingga tidak ada pembaruan yang
diharapkan: 1 // berarti diharapkan setidaknya 1 catatan dengan kunci dalam tabel db.

Di sini masalahnya adalah kueri mencoba memperbarui catatan untuk beberapa kunci, Tetapi hibernasi tidak menemukan catatan apa pun dengan kunci tersebut.

ParagFlume
sumber
Hai @ParagFlume, saya mendapat kesalahan yang sama "Pembaruan batch mengembalikan jumlah baris yang tidak terduga dari pembaruan: 0 jumlah baris aktual: 0 diharapkan: 1" ketika saya mencoba memilih satu objek dari databse saya, masalahnya hanya ada produksi, apakah Anda punya Ide tentang cara mengatasi masalah ini, saya dalam situasi kritis. Salam!
James
Saya pikir kueri tidak menemukan catatan apa pun dalam db, ia mengharapkan untuk menemukan satu catatan dalam db, yang coba diperbarui. Anda dapat memeriksa secara manual / query-browser apakah record benar-benar ada di db.
ParagFlume
ya catatannya ada, tetapi masalah saya adalah mengapa hibernasi mencoba memperbarui, saya hanya menggunakan layanan pilih untuk mendapatkan objek dari database!
Yakobus
mungkin Anda mencoba mengubah keadaan objek. dan sesi masih terbuka.
ParagFlume
bagaimana saya bisa memeriksa behaivor ini, karena saya bukan orang yang mengembangkan layanan ...
James
5

Seperti yang dikatakan Julius , ini terjadi ketika pembaruan Terjadi pada Objek yang turunannya dihapus. (Mungkin karena ada kebutuhan akan pembaruan untuk seluruh Objek Ayah dan terkadang kami lebih memilih untuk menghapus anak-anak dan memasukkannya kembali pada Ayah (baru, lama tidak masalah) bersama dengan pembaruan lain yang dapat dimiliki ayah pada salah satu itu bidang polos lainnya) Jadi ... agar ini berfungsi, hapus anak-anak (dalam Transaksi) dengan memanggil childrenList.clear()(Jangan mengulang melalui anak-anak dan menghapus masing-masing dengan beberapa childDAO.delete(childrenList.get(i).delete()))dan pengaturan @OneToMany(cascade = CascadeType.XXX ,orphanRemoval=true)di Sisi Objek Ayah. Kemudian perbarui ayah (fatherDAO.update (ayah)). (Ulangi untuk setiap objek ayah) Hasilnya adalah bahwa hubungan anak-anak dengan ayah mereka dilepas dan kemudian mereka dipindahkan sebagai yatim piatu oleh kerangka kerja.

George Papatheodorou
sumber
5

Saya mengalami masalah ini di mana kami memiliki satu-banyak hubungan.

Dalam file pemetaan hbm hibernate untuk master, untuk objek dengan pengaturan tipe set, ditambahkan cascade="save-update"dan berfungsi dengan baik.

Tanpa ini, secara default hibernasi mencoba memperbarui untuk catatan yang tidak ada dan dengan melakukan itu ia menyisipkan.

Pisau
sumber
5

Hal ini juga dapat terjadi ketika Anda mencoba untuk UPDATEsebuah PRIMARY KEY .

Marcel
sumber
4

Cara lain untuk mendapatkan kesalahan ini adalah jika Anda memiliki item null dalam koleksi.

Bryan Pugh
sumber
3

Saya mendapat masalah yang sama dan saya memverifikasi ini mungkin terjadi karena kunci utama kenaikan otomatis. Untuk mengatasi masalah ini, jangan masukkan nilai kenaikan otomatis dengan kumpulan data. Masukkan data tanpa kunci utama.

MacDaddy
sumber
2

Ini terjadi ketika Anda mencoba untuk menghapus objek yang sama dan kemudian memperbarui objek yang sama, gunakan ini setelah menghapus

session.clear ();

Sumit Singh
sumber
2

Ini terjadi pada saya juga, karena saya memiliki id saya sebagai Long, dan saya menerima dari tampilan nilai 0, dan ketika saya mencoba untuk menyimpan dalam database saya mendapatkan kesalahan ini, kemudian saya memperbaikinya dengan mengatur id ke null.

Roberto Rodriguez
sumber
1

Saya mengalami masalah ini ketika saya secara manual memulai dan melakukan transaksi di dalam metode yang dijelaskan sebagai @Transactional. Saya memperbaiki masalah dengan mendeteksi apakah transaksi aktif sudah ada.

//Detect underlying transaction
if (session.getTransaction() != null && session.getTransaction().isActive()) {
    myTransaction = session.getTransaction();
    preExistingTransaction = true;
} else {
    myTransaction = session.beginTransaction();
}

Kemudian saya mengizinkan Spring untuk menangani melakukan transaksi.

private void finishTransaction() {
    if (!preExistingTransaction) {
        try {
            tx.commit();
        } catch (HibernateException he) {
            if (tx != null) {
                tx.rollback();
            }
            log.error(he);
        } finally {
            if (newSessionOpened) {
                SessionFactoryUtils.closeSession(session);
                newSessionOpened = false;
                maxResults = 0;
            }
        }
    }
}
Gibado
sumber
1

Ini terjadi ketika Anda mendeklarasikan JSF Managed Bean sebagai

@RequestScoped;

kapan Anda harus mendeklarasikan sebagai

@SessionScoped;

Salam;

John Lopez
sumber
1

Saya mendapat kesalahan ini ketika saya mencoba memperbarui objek dengan id yang tidak ada di database. Alasan kesalahan saya adalah bahwa saya telah menetapkan properti dengan nama 'id' secara manual ke sisi klien JSON-representasi dari objek dan kemudian ketika deserialisasi objek di sisi server, properti 'id' ini akan menimpa variabel instan ( juga disebut 'id') yang seharusnya dihasilkan oleh Hibernate. Jadi berhati-hatilah terhadap benturan penamaan jika Anda menggunakan Hibernate untuk menghasilkan pengenal.

Soggiorno
sumber
1

Saya juga menemukan tantangan yang sama. Dalam kasus saya, saya memperbarui objek yang bahkan tidak ada, menggunakan hibernateTemplate.

Sebenarnya dalam aplikasi saya, saya mendapatkan objek DB untuk diperbarui. Dan saat memperbarui nilainya, saya juga memperbarui ID-nya secara tidak sengaja, dan melanjutkan untuk memperbaruinya dan menemukan kesalahan tersebut.

Saya menggunakan hibernateTemplateuntuk operasi CRUD.

Kuldeep Verma
sumber
1

Setelah membaca semua jawaban tidak menemukan siapa pun untuk berbicara tentang atribut terbalik dari hibernate.

Menurut pendapat saya, Anda juga harus memverifikasi dalam pemetaan hubungan Anda apakah kata kunci invers diatur dengan tepat. Kata kunci terbalik dibuat untuk menentukan sisi mana yang menjadi pemilik untuk menjaga hubungan. Prosedur untuk memperbarui dan memasukkan berbeda-beda sesuai dengan atribut ini.

Misalkan kita memiliki dua tabel:

principal_table , middle_table

dengan hubungan satu dengan banyak . Kelas pemetaan hiberntate adalah Principal dan Middle masing-masing.

Jadi kelas Kepala Sekolah memiliki SET objek Tengah . File pemetaan xml harus seperti berikut:

<hibernate-mapping>
    <class name="path.to.class.Principal" table="principal_table" ...>
    ...
    <set name="middleObjects" table="middle_table" inverse="true" fetch="select">
        <key>
            <column name="PRINCIPAL_ID" not-null="true" />
        </key>
        <one-to-many class="path.to.class.Middel" />
    </set>
    ...

Karena invers disetel ke "true", itu berarti kelas "Menengah" adalah pemilik hubungan, jadi kelas Kepala Sekolah TIDAK AKAN MEMPERBARUI hubungan tersebut.

Jadi prosedur update bisa diimplementasikan seperti ini:

session.beginTransaction();

Principal principal = new Principal();
principal.setSomething("1");
principal.setSomethingElse("2");


Middle middleObject = new Middle();
middleObject.setSomething("1");

middleObject.setPrincipal(principal);
principal.getMiddleObjects().add(middleObject);

session.saveOrUpdate(principal);
session.saveOrUpdate(middleObject); // NOTICE: you will need to save it manually

session.getTransaction().commit();

Ini berhasil untuk saya, tetapi Anda dapat menyarankan beberapa edisi untuk meningkatkan solusinya. Dengan begitu kita semua akan belajar.

Luis Teijon
sumber
1

Dalam kasus kami, kami akhirnya menemukan akar penyebab StaleStateException.

Faktanya kami menghapus baris dua kali dalam satu sesi hibernasi. Sebelumnya kami menggunakan ojdbc6 lib, dan ini ok dalam versi ini.

Tetapi ketika kami meningkatkan ke odjc7 atau ojdbc8, menghapus rekaman dua kali menimbulkan pengecualian. Ada bug di kode kami di mana kami menghapus dua kali, tapi itu tidak terbukti di ojdbc6.

Kami dapat mereproduksi dengan potongan kode ini:

Detail detail = getDetail(Long.valueOf(1396451));
session.delete(detail);
session.flush();
session.delete(detail);
session.flush();

Pada hibernasi siram pertama berjalan dan membuat perubahan dalam database. Selama 2 flush hibernate membandingkan objek sesi dengan rekaman tabel aktual, tetapi tidak dapat menemukannya, oleh karena itu pengecualian.

Sandeep Khantwal
sumber
1

Masalah ini terutama terjadi saat kita mencoba menyimpan atau memperbarui objek yang sudah diambil ke dalam memori oleh sesi yang sedang berjalan. Jika Anda telah mengambil objek dari sesi dan Anda mencoba untuk memperbarui di database, pengecualian ini mungkin dilemparkan.

Saya menggunakan session.evict (); untuk menghapus cache yang disimpan dalam mode hibernate terlebih dahulu atau jika tidak ingin mengambil resiko kehilangan data, lebih baik kamu membuat objek lain untuk menyimpan data temp.

     try
    {
        if(!session.isOpen())
        {
            session=EmployeyDao.getSessionFactory().openSession();
        }
            tx=session.beginTransaction();

        session.evict(e);
        session.saveOrUpdate(e);
        tx.commit();;
        EmployeyDao.shutDown(session);
    }
    catch(HibernateException exc)
    {
        exc.printStackTrace();
        tx.rollback();
    }
Amit Mishra
sumber
1

Hibernate 5.4.1 dan HHH-12878 masalah

Sebelum Hibernate 5.4.1, pengecualian kegagalan penguncian yang optimis (misalnya, StaleStateExceptionatauOptimisticLockException ) tidak menyertakan pernyataan gagal.

Masalah HHH-12878 dibuat untuk meningkatkan Hibernate sehingga saat melontarkan pengecualian penguncian yang optimis, PreparedStatementimplementasi JDBC juga dicatat:

if ( expectedRowCount > rowCount ) {
    throw new StaleStateException(
            "Batch update returned unexpected row count from update ["
                    + batchPosition + "]; actual row count: " + rowCount
                    + "; expected: " + expectedRowCount + "; statement executed: "
                    + statement
    );
}

Waktu Pengujian

Saya membuat BatchingOptimisticLockingTestdalam repositori GitHub Persistensi Java Kinerja Tinggi untuk mendemonstrasikan cara kerja perilaku baru.

Pertama, kita akan mendefinisikan Postentitas yang mendefinisikan @Versionproperti, sehingga memungkinkan mekanisme penguncian optimis implisit :

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    private String title;

    @Version
    private short version;

    public Long getId() {
        return id;
    }

    public Post setId(Long id) {
        this.id = id;
        return this;
    }

    public String getTitle() {
        return title;
    }

    public Post setTitle(String title) {
        this.title = title;
        return this;
    }

    public short getVersion() {
        return version;
    }
}

Kami akan mengaktifkan pengumpulan JDBC menggunakan 3 properti konfigurasi berikut:

properties.put("hibernate.jdbc.batch_size", "5");
properties.put("hibernate.order_inserts", "true");
properties.put("hibernate.order_updates", "true");

Kami akan membuat 3 Postentitas:

doInJPA(entityManager -> {
    for (int i = 1; i <= 3; i++) {
        entityManager.persist(
            new Post()
                .setTitle(String.format("Post no. %d", i))
        );
    }
});

Dan Hibernate akan menjalankan penyisipan batch JDBC:

SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')

Query: [
    INSERT INTO post (title, version, id) 
    VALUES (?, ?, ?)
], 
Params:[
    (Post no. 1, 0, 1), 
    (Post no. 2, 0, 2), 
    (Post no. 3, 0, 3)
]

Jadi, kami tahu bahwa batching JDBC berfungsi dengan baik.

Sekarang, mari kita tiru masalah penguncian optimis:

doInJPA(entityManager -> {
    List<Post> posts = entityManager.createQuery("""
        select p 
        from Post p
        """, Post.class)
    .getResultList();

    posts.forEach(
        post -> post.setTitle(
            post.getTitle() + " - 2nd edition"
        )
    );

    executeSync(
        () -> doInJPA(_entityManager -> {
            Post post = _entityManager.createQuery("""
                select p 
                from Post p
                order by p.id
                """, Post.class)
            .setMaxResults(1)
            .getSingleResult();

            post.setTitle(post.getTitle() + " - corrected");
        })
    );
});

Transaksi pertama memilih semua Postentitas dan mengubah titleproperti.

Namun, sebelum yang pertama EntityManagerdihapus, kita akan menjalankan transisi kedua menggunakan executeSyncmetode tersebut.

Transaksi kedua mengubah yang pertama Post, jadi versionakan bertambah:

Query:[
    UPDATE 
        post 
    SET 
        title = ?, 
        version = ? 
    WHERE 
        id = ? AND 
        version = ?
], 
Params:[
    ('Post no. 1 - corrected', 1, 1, 0)
]

Sekarang, ketika transaksi pertama mencoba untuk membersihkan EntityManager, kita akan mendapatkan OptimisticLockException:

Query:[
    UPDATE 
        post 
    SET 
        title = ?, 
        version = ? 
    WHERE 
        id = ? AND 
        version = ?
], 
Params:[
    ('Post no. 1 - 2nd edition', 1, 1, 0), 
    ('Post no. 2 - 2nd edition', 1, 2, 0), 
    ('Post no. 3 - 2nd edition', 1, 3, 0)
]

o.h.e.j.b.i.AbstractBatchImpl - HHH000010: On release of batch it still contained JDBC statements

o.h.e.j.b.i.BatchingBatch - HHH000315: Exception executing batch [
    org.hibernate.StaleStateException: 
    Batch update returned unexpected row count from update [0]; 
    actual row count: 0; 
    expected: 1; 
    statement executed: 
        PgPreparedStatement [
            update post set title='Post no. 3 - 2nd edition', version=1 where id=3 and version=0
        ]
], 
SQL: update post set title=?, version=? where id=? and version=?

Jadi, Anda perlu memutakhirkan ke Hibernate 5.4.1 atau yang lebih baru untuk mendapatkan manfaat dari peningkatan ini.

Vlad Mihalcea
sumber
0

Ini terjadi jika Anda mengubah sesuatu dalam kumpulan data menggunakan kueri sql asli tetapi objek yang bertahan untuk kumpulan data yang sama ada di cache sesi. Gunakan session.evict (yourObject);

abhi
sumber
0

Salah satunya

SessionFactory sf=new Configuration().configure().buildSessionFactory();
Session session=sf.openSession();

UserDetails user=new UserDetails();

session.beginTransaction();
user.setUserName("update user agian");
user.setUserId(12);
session.saveOrUpdate(user);
session.getTransaction().commit();
System.out.println("user::"+user.getUserName());

sf.close();
vootla561.dll
sumber
0

Saya menghadapi pengecualian ini, dan hibernasi berfungsi dengan baik. Saya mencoba memasukkan satu catatan secara manual menggunakan pgAdmin, di sini masalahnya menjadi jelas. Kueri penyisipan SQL mengembalikan 0 penyisipan. dan ada fungsi pemicu yang menyebabkan masalah ini karena mengembalikan null. jadi saya hanya perlu mengaturnya kembali baru. dan akhirnya saya memecahkan masalah tersebut.

harapan yang membantu tubuh mana pun.

kamel2005
sumber
0

Saya mendapat kesalahan ini karena saya keliru memetakan IDkolom menggunakanId(x => x.Id, "id").GeneratedBy.**Assigned**();

Masalah diselesaikan dengan menggunakan Id(x => x.Id, "id").GeneratedBy.**Identity**();

roylac
sumber
0

Ini terjadi pada saya karena saya kehilangan deklarasi ID di kelas kacang.

Ganesh Giri
sumber
0

Dalam kasus saya, ada masalah dengan Database karena salah satu Stored Procs menghabiskan semua CPU yang menyebabkan waktu respons DB tinggi. Setelah ini terbunuh, masalah diselesaikan.

Amit
sumber