Mengapa Hibernate Open Session in View dianggap sebagai praktik yang buruk?

108

Dan jenis strategi alternatif apa yang Anda gunakan untuk menghindari LazyLoadExceptions?

Saya mengerti bahwa sesi terbuka yang dilihat memiliki masalah dengan:

  • Aplikasi berlapis yang berjalan di jvm berbeda
  • Transaksi dilakukan hanya di akhir, dan kemungkinan besar Anda akan menyukai hasilnya sebelumnya.

Tetapi, jika Anda tahu bahwa aplikasi Anda berjalan pada satu vm, mengapa tidak meringankan rasa sakit Anda dengan menggunakan sesi terbuka dalam strategi tampilan?

HeDinges
sumber
12
Apakah OSIV dianggap praktik yang buruk? Oleh siapa?
Johannes Brodwall
4
Dan - apa alternatif yang baik?
David Rabinowitz
7
Kedamaian teks ini jika dari pengembang jahitan: Ada beberapa masalah dengan implementasi ini, yang paling serius adalah kita tidak pernah bisa memastikan bahwa transaksi berhasil sampai kita melakukannya tetapi pada saat transaksi "sesi terbuka dalam tampilan" dilakukan, tampilan sepenuhnya dirender, dan respons yang diberikan mungkin sudah dialihkan ke klien. Bagaimana kami dapat memberi tahu pengguna bahwa transaksi mereka tidak berhasil?
darpet
2
Lihat posting blog ini untuk pro dan kontra dan pengalaman saya sendiri tentang itu - blog.jhades.org/open-session-in-view-pattern-pros-and-cons
Angular University

Jawaban:

46

Karena mengirim Proxies yang mungkin tidak diinisialisasi, terutama koleksi, di lapisan tampilan dan memicu pemuatan hibernasi dari sana dapat mengganggu baik dari sudut pandang kinerja maupun pemahaman.

Pengertian :

Menggunakan OSIV 'mengotori' lapisan tampilan dengan masalah yang terkait dengan lapisan akses data.

Lapisan tampilan tidak bersiap untuk menangani HibernateExceptionyang mungkin terjadi saat pemuatan lambat, tetapi mungkin lapisan akses datanya.

Performa :

OSIV cenderung menarik pemuatan entitas yang tepat di bawah karpet - Anda cenderung tidak memperhatikan bahwa koleksi atau entitas Anda dimulai dengan malas (mungkin N + 1). Lebih nyaman, lebih sedikit kontrol.


Pembaruan: lihat Antipattern OpenSessionInView untuk diskusi yang lebih besar mengenai subjek ini. Penulis membuat daftar tiga poin penting:

  1. setiap inisialisasi malas akan memberi Anda kueri yang berarti setiap entitas akan membutuhkan N + 1 kueri, di mana N adalah jumlah asosiasi malas. Jika layar Anda menampilkan data tabel, membaca log Hibernate adalah petunjuk besar bahwa Anda tidak melakukan apa yang seharusnya
  2. ini benar-benar mengalahkan arsitektur berlapis, karena Anda menodai kuku Anda dengan DB di lapisan presentasi. Ini adalah tipuan konseptual, jadi saya bisa menerimanya tetapi ada konsekuensi wajarnya
  3. last but not least, jika pengecualian terjadi saat mengambil sesi, itu akan terjadi selama penulisan halaman: Anda tidak dapat menampilkan halaman kesalahan yang bersih kepada pengguna dan satu-satunya hal yang dapat Anda lakukan adalah menulis pesan kesalahan di badan
Robert Munteanu
sumber
13
Oke, itu 'mencemari' lapisan tampilan dengan pengecualian hibernasi. Tapi, mengenai kinerja, saya pikir masalahnya sangat mirip daripada mengakses lapisan layanan yang akan mengembalikan dto Anda. Jika Anda menghadapi masalah kinerja, Anda harus mengoptimalkan masalah spesifik tersebut dengan kueri yang lebih cerdas atau dto yang lebih ringan. Jika Anda harus mengembangkan terlalu banyak metode layanan untuk menangani kemungkinan yang mungkin Anda perlukan dalam tampilan, Anda juga 'mencemari' lapisan layanan. tidak?
HeDinges
1
Satu perbedaan adalah penundaan penutupan sesi Hibernasi. Anda akan menunggu JSP dirender / ditulis / etc, dan itu membuat objek dalam memori lebih lama. Itu mungkin menjadi masalah terutama jika Anda perlu menulis data pada komit sesi.
Robert Munteanu
8
Tidak masuk akal untuk mengatakan bahwa OSIV merusak Performa. Alternatif apa yang ada kecuali untuk menggunakan DTO? Dalam hal ini, Anda akan selalu memiliki kinerja yang lebih rendah karena data yang digunakan oleh setiap tampilan harus dimuat bahkan untuk tampilan yang tidak membutuhkannya.
Johannes Brodwall
11
Saya pikir polusi bekerja sebaliknya. Jika saya ingin memuat data dengan cepat, lapisan logika (atau lebih buruk lapisan akses data) perlu mengetahui cara suatu objek akan ditampilkan. Ubah tampilan dan Anda akhirnya memuat hal-hal yang tidak Anda perlukan atau objek yang Anda butuhkan hilang. Hibernate Exception adalah bug dan sama beracunnya dengan pengecualian tak terduga lainnya. Tapi kinerja adalah masalah. Masalah kinerja dan skalabilitas akan memaksa Anda untuk lebih memikirkan dan bekerja di lapisan akses data Anda, dan mungkin memaksa sesi ditutup lebih awal
Jens Schauder
1
@JensSchauder "Ubah tampilan dan Anda akhirnya memuat hal-hal yang tidak Anda perlukan atau objek yang Anda butuhkan hilang". Inilah tepatnya. Jika Anda mengubah tampilan, jauh lebih baik untuk memuat hal-hal yang tidak Anda perlukan (karena Anda lebih cenderung ingin mengambilnya) atau mencari tahu objek yang hilang karena Anda akan mendapatkan pengecualian pemuatan lambat, daripada membiarkan tampilan dimuat malas karena itu akan menghasilkan masalah N + 1, dan Anda bahkan tidak akan tahu itu terjadi. Jadi IMO lebih baik lapisan layanan (dan Anda) tahu apa yang dikirim daripada tampilan memuat dengan malas dan Anda tidak tahu apa-apa tentang itu.
Jeshurun
40

Untuk penjelasan yang lebih panjang, Anda dapat membaca artikel Open Session In View Anti-Pattern saya . Jika tidak, berikut adalah ringkasan mengapa Anda tidak boleh menggunakan Sesi Terbuka Dalam Tampilan.

Sesi Terbuka Dalam Tampilan mengambil pendekatan yang buruk untuk mengambil data. Alih-alih membiarkan lapisan bisnis memutuskan cara terbaik untuk mengambil semua pengaitan yang dibutuhkan oleh lapisan Tampilan, ini memaksa Konteks Persistensi untuk tetap terbuka sehingga lapisan Tampilan dapat memicu inisialisasi Proxy.

masukkan deskripsi gambar di sini

  • The OpenSessionInViewFiltermenyebut openSessionmetode yang mendasari SessionFactorydan memperoleh baru Session.
  • Itu Sessionterikat ke TransactionSynchronizationManager.
  • The OpenSessionInViewFiltermenyebut doFilterdari javax.servlet.FilterChainreferensi objek dan permintaan itu diproses lebih lanjut
  • The DispatcherServletdisebut, dan rute permintaan HTTP ke mendasari PostController.
  • The PostControllerpanggilan PostServiceuntuk mendapatkan daftar Postentitas.
  • The PostServicemembuka transaksi baru, dan HibernateTransactionManagermenggunakan kembali sama Sessionyang dibuka oleh OpenSessionInViewFilter.
  • The PostDAOmenjemput daftar Postentitas tanpa menginisialisasi setiap asosiasi malas.
  • The PostServicemelakukan transaksi yang mendasarinya, tetapi Sessiontidak tertutup karena dibuka secara eksternal.
  • The DispatcherServletdimulai rendering UI, yang, pada gilirannya, menavigasi asosiasi malas dan memicu inisialisasi mereka.
  • The OpenSessionInViewFilterdapat menutup Session, dan koneksi database yang mendasari dilepaskan juga.

Pada pandangan pertama, ini mungkin tidak terlihat seperti hal yang buruk untuk dilakukan, tetapi, setelah Anda melihatnya dari perspektif database, serangkaian kekurangan mulai menjadi lebih jelas.

Lapisan layanan membuka dan menutup transaksi database, tetapi setelah itu, tidak ada transaksi eksplisit yang terjadi. Karena alasan ini, setiap pernyataan tambahan yang dikeluarkan dari fase rendering UI dijalankan dalam mode komit otomatis. Komitmen otomatis memberi tekanan pada server database karena setiap pernyataan harus membuang log transaksi ke disk, sehingga menyebabkan banyak lalu lintas I / O di sisi database. Satu optimasi akan menandai Connectionsebagai read-only yang akan memungkinkan server database untuk menghindari penulisan ke log transaksi.

Tidak ada lagi pemisahan masalah karena pernyataan dibuat oleh lapisan layanan dan proses rendering UI. Menulis pengujian integrasi yang menegaskan jumlah pernyataan yang dihasilkan harus melalui semua lapisan (web, layanan, DAO), sementara aplikasi diterapkan di wadah web. Bahkan ketika menggunakan database dalam memori (misalnya HSQLDB) dan web server yang ringan (misalnya Jetty), pengujian integrasi ini akan lebih lambat untuk dieksekusi daripada jika lapisan dipisahkan dan pengujian integrasi back-end menggunakan database, sedangkan pengujian integrasi front-end mengejek lapisan layanan sama sekali.

Lapisan UI terbatas untuk menavigasi asosiasi yang pada gilirannya dapat memicu masalah kueri N + 1. Meskipun Hibernate menawarkan @BatchSizeuntuk mengambil pengaitan dalam kelompok, dan FetchMode.SUBSELECTuntuk mengatasi skenario ini, anotasi memengaruhi rencana pengambilan default, sehingga anotasi tersebut diterapkan ke setiap kasus penggunaan bisnis. Untuk alasan ini, kueri lapisan akses data jauh lebih cocok karena dapat disesuaikan untuk kebutuhan pengambilan data kasus penggunaan saat ini.

Last but not least, koneksi database dapat diadakan selama fase rendering UI (tergantung pada mode rilis koneksi Anda) yang meningkatkan waktu sewa koneksi dan membatasi throughput transaksi secara keseluruhan karena kemacetan pada kumpulan koneksi database. Semakin banyak koneksi ditahan, semakin banyak permintaan bersamaan lainnya yang akan menunggu untuk mendapatkan koneksi dari pool.

Jadi, jika koneksi Anda ditahan terlalu lama, Anda bisa memperoleh / melepaskan beberapa koneksi untuk satu permintaan HTTP, oleh karena itu memberi tekanan pada kumpulan koneksi yang mendasarinya dan membatasi skalabilitas.

Sepatu Musim Semi

Sayangnya, Sesi Terbuka dalam Tampilan diaktifkan secara default di Spring Boot .

Jadi, pastikan bahwa di application.propertiesfile konfigurasi, Anda memiliki entri berikut:

spring.jpa.open-in-view=false

Ini akan menonaktifkan OSIV, sehingga Anda dapat menangani dengan LazyInitializationExceptioncara yang benar .

Vlad Mihalcea
sumber
3
Menggunakan Open Session in View dengan auto-commit dimungkinkan tetapi tidak seperti yang dimaksudkan oleh pengembang Hibernate. Jadi, meskipun Sesi Terbuka dalam Tampilan memiliki kekurangannya, komit otomatis bukan salah satunya karena Anda bisa mematikannya dan tetap menggunakannya.
Stefan.m
Anda berbicara tentang apa yang terjadi di dalam transaksi, dan itu benar. Tetapi fase rendering Lapisan Web terjadi di luar Hibernate, maka Anda mendapatkan mode autocommit. Masuk akal?
Vlad Mihalcea
Saya pikir itu adalah varian yang tidak optimal untuk Sesi Terbuka dalam Tampilan. Sesi dan transaksi harus tetap terbuka hingga tampilan telah dirender, maka mode autocommit tidak diperlukan.
Stefan.m
2
Sesi tetap terbuka. Tapi transaksi tidak. Jangkauan transaksi di seluruh proses juga tidak optimal karena ini menambah panjangnya dan kunci ditahan lebih lama dari yang diperlukan. Bayangkan apa yang terjadi jika tampilan menampilkan RuntimeException. Akankah transaksi dibatalkan karena rendering UI gagal?
Vlad Mihalcea
Terima kasih banyak atas jawaban yang sangat rinci! Saya hanya akan mengubah panduan pada akhirnya karena pengguna boot musim semi kemungkinan besar tidak akan menggunakan jpa dengan cara itu.
Skeeve
24
  • transaksi dapat dilakukan di lapisan layanan - transaksi tidak terkait dengan OSIV. Itu Sessionyang tetap terbuka, bukan transaksi - berjalan.

  • jika lapisan aplikasi Anda tersebar di beberapa mesin, maka Anda tidak dapat menggunakan OSIV - Anda harus menginisialisasi semua yang Anda butuhkan sebelum mengirim objek melalui kabel.

  • OSIV adalah cara yang bagus dan transparan (yaitu - tidak ada kode Anda yang menyadari bahwa hal itu terjadi) untuk memanfaatkan manfaat kinerja dari pemuatan lambat

Bozho
sumber
2
Mengenai poin pertama, ini setidaknya tidak benar untuk OSIV asli dari wiki JBoss, ini juga menangani demarkasi transaksi di sekitar permintaan.
Pascal Thivent
@PascalThivent Bagian mana yang membuat Anda berpikir demikian?
Sanghyun Lee
13

Saya tidak akan mengatakan bahwa Open Session In View dianggap sebagai praktik yang buruk; apa yang memberimu kesan itu?

Open-Session-In-View adalah pendekatan sederhana untuk menangani sesi dengan Hibernate. Karena sederhana, terkadang juga sederhana. Jika Anda memerlukan kontrol yang sangat cermat atas transaksi Anda, seperti memiliki beberapa transaksi dalam satu permintaan, Open-Session-In-View tidak selalu merupakan pendekatan yang baik.

Seperti yang ditunjukkan orang lain, ada beberapa trade-off untuk OSIV - Anda jauh lebih rentan terhadap masalah N + 1 karena Anda cenderung tidak menyadari transaksi apa yang Anda mulai. Pada saat yang sama, ini berarti Anda tidak perlu mengubah lapisan layanan Anda untuk beradaptasi dengan perubahan kecil pada tampilan Anda.

Geoffrey Wiseman
sumber
5

Jika Anda menggunakan wadah Inversion of Control (IoC) seperti Spring, Anda mungkin ingin membaca tentang pelingkupan kacang . Pada dasarnya, saya memberi tahu Spring untuk memberi saya Sessionobjek Hibernate yang siklus hidupnya mencakup seluruh permintaan (yaitu, dibuat dan dihancurkan pada awal dan akhir permintaan HTTP). Saya tidak perlu khawatir tentang LazyLoadExceptionatau menutup sesi karena wadah IoC mengaturnya untuk saya.

Seperti disebutkan, Anda harus memikirkan masalah kinerja N + 1 SELECT. Anda selalu dapat mengkonfigurasi entitas Hibernate Anda setelah itu untuk melakukan pemuatan gabungan yang bersemangat di tempat-tempat di mana kinerja menjadi masalah.

Solusi pelingkupan kacang bukan khusus Musim Semi. Saya tahu PicoContainer menawarkan kemampuan yang sama dan saya yakin wadah IoC dewasa lainnya menawarkan sesuatu yang serupa.

0sumgain
sumber
1
Apakah Anda memiliki pointer ke implementasi aktual sesi Hibernate yang tersedia dalam tampilan melalui request scoped beans?
Marvo
4

Menurut pengalaman saya sendiri, OSIV tidak terlalu buruk. Satu-satunya pengaturan yang saya buat adalah menggunakan dua transaksi berbeda: - yang pertama, dibuka di "lapisan layanan", di mana saya memiliki "logika bisnis" - yang kedua dibuka tepat sebelum rendering tampilan

Davide
sumber
3

Saya baru saja membuat posting tentang beberapa pedoman tentang kapan menggunakan sesi terbuka dalam tampilan di blog saya. Lihat jika Anda tertarik.

http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/

Chris Upton
sumber
1
Sebagai pedoman umum SO, jika Anda memberikan jawaban, sebaiknya lakukan lebih dari sekadar menautkan di tempat lain. Mungkin memberikan satu atau dua kalimat atau item terdaftar yang memberikan intinya. Tidak masalah untuk menautkan, tetapi Anda ingin memberikan sedikit nilai tambahan. Jika tidak, Anda mungkin hanya ingin memberi komentar dan meletakkan tautan di sana.
DWright
tautan dalam jawaban ini layak dibaca, ini memberikan panduan yang baik tentang kapan harus menggunakan OSIV dan tidak
ams
1

Saya sangat berkarat pada Hibernate .. tapi saya pikir mungkin untuk memiliki banyak transaksi dalam satu sesi Hibernate. Jadi, batasan transaksi Anda tidak harus sama dengan peristiwa awal / penghentian sesi.

OSIV, imo, terutama berguna karena kita dapat menghindari penulisan kode untuk memulai 'konteks persistensi' (alias sesi) setiap kali permintaan perlu membuat akses DB.

Di lapisan layanan Anda, Anda mungkin perlu melakukan panggilan ke metode yang memiliki kebutuhan transaksi berbeda, seperti 'Wajib, Wajib Baru, dll.' Satu-satunya hal yang diperlukan metode ini adalah bahwa seseorang (yaitu filter OSIV) telah memulai konteks ketekunan, sehingga satu-satunya hal yang perlu mereka khawatirkan adalah - "hei, beri saya sesi hibernasi untuk utas ini .. Saya perlu melakukan beberapa Barang DB ".

rjk2008
sumber
1

Ini tidak akan banyak membantu tetapi Anda dapat memeriksa topik saya di sini: * Hibernate Cache1 OutOfMemory dengan OpenSessionInView

Saya memiliki beberapa masalah OutOfMemory karena OpenSessionInView dan banyak entitas dimuat, karena mereka tetap berada di cache Hibernate level1 dan tidak dikumpulkan sampah (saya memuat banyak entitas dengan 500 item per halaman, tetapi semua entitas tetap dalam cache)

Sebastien Lorber
sumber