Penggunaan flush () yang benar di JPA / Hibernate

110

Saya sedang mengumpulkan informasi tentang metode flush (), tetapi saya tidak begitu jelas kapan harus menggunakannya dan bagaimana menggunakannya dengan benar. Dari apa yang saya baca, pemahaman saya adalah bahwa konten konteks ketekunan akan disinkronkan dengan database, yaitu mengeluarkan pernyataan yang luar biasa atau menyegarkan data entitas.

Sekarang saya mendapat skenario berikut dengan dua entitas Adan B(dalam hubungan satu-ke-satu, tetapi tidak diberlakukan atau dimodelkan oleh JPA). Amemiliki PK gabungan, yang disetel secara manual, dan juga memiliki bidang IDENTITAS yang dibuat secara otomatis recordId. Ini recordIdharus ditulis ke entitas Bsebagai kunci asing ke A. Saya menabung Adan Bdalam satu transaksi. Masalahnya adalah bahwa nilai yang dihasilkan otomatis A.recordIdtidak tersedia dalam transaksi, kecuali saya membuat panggilan eksplisit em.flush()setelah menelepon em.persist()di A. (Jika saya memiliki IDENTITAS PK yang dibuat secara otomatis, maka nilainya langsung diperbarui di entitas, tetapi tidak demikian di sini.)

Apakah bisa em.flush()menyebabkan kerugian saat menggunakannya dalam transaksi?

MicSim
sumber

Jawaban:

148

Mungkin detail persisnya em.flush()bergantung pada implementasi. Secara umum, penyedia JPA seperti Hibernate dapat menyimpan instruksi SQL yang seharusnya mereka kirim ke database, seringkali sampai Anda benar-benar melakukan transaksi. Misalnya, Anda memanggil em.persist(), Hibernate ingat itu harus membuat database INSERT, tetapi tidak benar-benar menjalankan instruksi sampai Anda melakukan transaksi. Afaik, ini terutama dilakukan untuk alasan kinerja.

Dalam beberapa kasus Anda ingin instruksi SQL dijalankan segera; umumnya ketika Anda membutuhkan hasil dari beberapa efek samping, seperti kunci yang dibuat secara otomatis, atau pemicu database.

Apa yang em.flush()dilakukan adalah mengosongkan cache instruksi SQL internal, dan segera menjalankannya ke database.

Intinya: tidak ada kerusakan yang dilakukan, hanya Anda yang dapat mengalami kinerja (kecil) karena Anda mengganti keputusan penyedia JPA sehubungan dengan waktu terbaik untuk mengirim instruksi SQL ke database.

Flavio
sumber
1
Apa yang dia katakan. perilaku em.flush () menggemakan java.io.Flushable.flush () di mana semua data yang di-buffer dikirim ke tujuan mana pun yang sesuai.
Erik
4
jika flush () mengirim data ke database? apa yang terjadi jika pengecualian dilemparkan setelah itu? Akankah manajer entitas mengembalikan semuanya? bahkan data yang ditulis pada flush pertama?
Jaime Hablutzel
101
flush () mengirimkan instruksi SQL ke database seperti INSERT, UPDATE dll. Itu tidak akan mengirim COMMIT, jadi jika Anda memiliki pengecualian setelah flush (), Anda masih dapat melakukan rollback lengkap.
Flavio
17
Anda dapat mengembalikan DB, tetapi tidak akan mengembalikan perubahan apa pun ke objek, misalnya, 'versi' yang bertambah otomatis, ID yang dibuat secara otomatis, dll. Selanjutnya, manajer entitas akan ditutup setelah rollback. Berhati-hatilah jika Anda mencoba untuk 'menggabungkan' objek ke sesi lain, 'versi' yang bertambah otomatis khususnya dapat menyebabkan OptimisticLockException.
Peter Davis
11
Selain memicu efek samping, alasan lain untuk menggunakan flush () adalah jika Anda ingin dapat membaca efek operasi dalam database menggunakan JPQL / HQL (misalnya dalam pengujian). JPA tidak dapat menggunakan data yang di-cache saat menjalankan kueri ini, jadi hanya hal-hal yang sebenarnya ada di DB yang akan dibaca.
sleske
2

Sebenarnya, em.flush()melakukan lebih dari sekedar mengirim perintah SQL yang di-cache. Ia mencoba untuk menyinkronkan konteks ketekunan ke database yang mendasarinya. Ini dapat menyebabkan banyak konsumsi waktu pada proses Anda jika cache Anda berisi koleksi untuk disinkronkan.

Perhatian saat menggunakannya.

Ricardo Nakashima
sumber
1

Dapatkah em.flush () menyebabkan kerugian saat menggunakannya dalam transaksi?

Ya, itu mungkin menahan kunci dalam database untuk durasi yang lebih lama dari yang diperlukan.

Umumnya, Saat menggunakan JPA, Anda mendelegasikan manajemen transaksi ke container (alias CMT - menggunakan anotasi @Transactional pada metode bisnis) yang berarti bahwa transaksi secara otomatis dimulai saat memasuki metode dan berkomitmen / dibatalkan di bagian akhir. Jika Anda membiarkan EntityManager menangani sinkronisasi database, eksekusi pernyataan sql hanya akan dipicu sebelum komit, yang mengarah ke penguncian singkat dalam database. Jika tidak, operasi tulis yang dibilas secara manual dapat mempertahankan kunci antara pembilasan manual dan pengaktifan otomatis yang dapat berlangsung lama sesuai dengan waktu eksekusi metode yang tersisa.

Perhatikan bahwa beberapa operasi secara otomatis memicu pembilasan: menjalankan kueri asli pada sesi yang sama (status EM harus di-flush agar dapat dijangkau oleh kueri SQL), memasukkan entitas menggunakan id yang dihasilkan asli (dihasilkan oleh database, jadi pernyataan sisipkan harus dipicu sehingga EM dapat mengambil id yang dihasilkan dan mengelola hubungan dengan benar)

Mengobrol
sumber