Hibernasi desain aplikasi pemuatan lambat

87

Saya cenderung menggunakan Hibernate dalam kombinasi dengan kerangka kerja Spring dan kemampuan demarkasi transaksi deklaratifnya (misalnya, @Transactional ).

Seperti yang kita semua ketahui, hibernasi berusaha menjadi non-invasif dan setransparan mungkin, namun ini terbukti sedikit lebih menantang saat menggunakan lazy-loadedhubungan.


Saya melihat sejumlah alternatif desain dengan tingkat transparansi berbeda.

  1. Jadikan hubungan tidak lambat (mis., fetchType=FetchType.EAGER)
    • Ini membatalkan keseluruhan gagasan pemuatan lambat ..
  2. Inisialisasi koleksi menggunakan Hibernate.initialize(proxyObj);
    • Ini menyiratkan kopling yang relatif tinggi ke DAO
    • Meskipun kita bisa mendefinisikan antarmuka dengan initialize, implementasi lain tidak dijamin akan memberikan yang setara.
  3. Tambahkan perilaku transaksi ke Modelobjek persisten itu sendiri (menggunakan proxy dinamis atau @Transactional)
    • Saya belum mencoba pendekatan proxy dinamis, meskipun saya sepertinya tidak pernah mendapatkan @Transactional mengerjakan objek persisten itu sendiri. Mungkin karena hibernasi itu adalah operasi pada proxy yang akan digunakan.
    • Kehilangan kendali saat transaksi benar-benar terjadi
  4. Sediakan API lazy / non-lazy, misalnya, loadData()danloadDataWithDeps()
    • Memaksa aplikasi untuk mengetahui kapan harus menggunakan rutin yang mana, lagi kopling yang ketat
    • Metode overflow loadDataWithA(), ....,loadDataWithX()
  5. Paksa pencarian dependensi, misalnya dengan hanya menyediakan byId()operasi
    • Membutuhkan banyak rutinitas yang berorientasi non-objek, misalnya, findZzzById(zid)dan kemudian getYyyIds(zid)bukannyaz.getY()
    • Akan berguna untuk mengambil setiap objek dalam koleksi satu per satu jika ada overhead pemrosesan yang besar di antara transaksi.
  6. Jadikan bagian dari aplikasi @Transactional, bukan hanya DAO
    • Pertimbangan yang mungkin untuk transaksi bersarang
    • Membutuhkan rutinitas yang disesuaikan untuk manajemen transaksi (misalnya, cukup kecil)
    • Dampak programatik kecil, meskipun dapat mengakibatkan transaksi besar
  7. Berikan DAO dengan profil pengambilan dinamis , misalnya,loadData(id, fetchProfile);
    • Aplikasi harus mengetahui profil mana yang akan digunakan kapan
  8. Jenis transaksi AoP, misalnya, operasi intersepsi dan melakukan transaksi bila diperlukan
    • Memerlukan manipulasi kode byte atau penggunaan proxy
    • Kehilangan kendali saat transaksi dilakukan
    • Ilmu hitam, seperti biasa :)

Apakah saya melewatkan opsi apa pun?


Manakah pendekatan pilihan Anda saat mencoba meminimalkan dampak lazy-loadedhubungan dalam desain aplikasi Anda?

(Oh, dan maaf untuk WoT )

Johan Sjöberg
sumber
contoh untuk opsi 2 & 5: m-hewedy.blogspot.ch/2010/03/…
Adrien Be
Bisakah Anda memberikan contoh untuk opsi 4?
derajatightdc

Jawaban:

26

Seperti yang kita semua tahu, hibernasi berusaha menjadi non-invasif dan setransparan mungkin

Saya akan mengatakan asumsi awal salah. Persistensi transaparent adalah mitos, karena aplikasi harus selalu menjaga siklus hidup entitas dan ukuran grafik objek yang dimuat.

Perhatikan bahwa Hibernate tidak dapat membaca pikiran, oleh karena itu jika Anda mengetahui bahwa Anda memerlukan sekumpulan dependensi tertentu untuk operasi tertentu, Anda perlu mengungkapkan niat Anda untuk Hibernate.

Dari sudut pandang ini, solusi yang mengungkapkan maksud ini secara eksplisit (yaitu, 2, 4 dan 7) terlihat masuk akal dan tidak menderita karena kurangnya transparansi.

axtavt
sumber
Anda benar tentu saja, setransparan mungkin hanya berfungsi sejauh ini. Itu adalah beberapa pilihan bagus yang Anda pilih.
Johan Sjöberg
IMHO: jawaban yang sangat benar. Memang, itu hanya mitos. BTW: pilihan saya adalah untuk opsi 4 dan 7 (atau menjauh dari ORM sama sekali)
G. Demecki
7

Saya tidak yakin masalah mana (yang disebabkan oleh kemalasan) yang Anda maksud, tetapi bagi saya rasa sakit terbesar adalah menghindari kehilangan konteks sesi dalam cache aplikasi saya sendiri. Kasus tipikal:

  • objek foodimuat dan dimasukkan ke dalam peta;
  • utas lain mengambil objek ini dari peta dan memanggil foo.getBar()(sesuatu yang tidak pernah dipanggil sebelumnya dan dievaluasi secara malas);
  • ledakan!

Jadi, untuk mengatasi ini kami memiliki sejumlah aturan:

  • bungkus sesi sejelas mungkin (misalnya OpenSessionInViewFilteruntuk webapps);
  • memiliki API umum untuk kumpulan utas / utas di mana sesi db bind / unbind dilakukan di suatu tempat yang tinggi dalam hierarki (terbungkus try/finally) sehingga subclass tidak perlu memikirkannya;
  • saat meneruskan objek di antara utas, teruskan ID alih-alih objek itu sendiri. Untaian penerima dapat memuat objek jika perlu;
  • saat menyimpan objek ke cache, jangan pernah menyimpan objek dalam cache kecuali idnya. Miliki metode abstrak di DAO atau kelas manajer Anda untuk memuat objek dari cache Hibernasi tingkat 2 saat Anda mengetahui ID-nya. Biaya mengambil objek dari cache Hibernate tingkat 2 masih jauh lebih murah daripada pergi ke DB.

Ini, seperti yang Anda lihat, memang tidak mendekati non-invasif dan transparan . Tapi biayanya masih lumayan, dibandingkan dengan harga yang harus saya bayar untuk eager loading. Masalah dengan yang terakhir adalah terkadang hal itu mengarah pada efek kupu-kupu saat memuat objek referensi tunggal, apalagi kumpulan entitas. Konsumsi memori, penggunaan CPU, dan latensi juga jauh lebih buruk, jadi saya rasa saya bisa menerimanya.

mindas
sumber
Terima kasih untuk balasan Anda. Hilangnya transparencyadalah dari memaksa aplikasi untuk memperhatikan pemuatan objek yang lambat. Jika semuanya diambil dengan penuh semangat, aplikasi dapat sepenuhnya tidak menyadari apakah objek disimpan ke dalam database atau tidak, karena Foo.getBar()akan selalu berhasil. > when passing objects between threads, pass IDs, ya, ini akan sesuai dengan # 5.
Johan Sjöberg
3

Pola yang sangat umum adalah menggunakan OpenEntityManagerInViewFilter jika Anda sedang membuat aplikasi web.

Jika Anda membangun layanan, saya akan membuka TX pada metode publik layanan, bukan pada DAO, karena metode yang sangat sering diperlukan untuk mendapatkan atau memperbarui beberapa entitas.

Ini akan menyelesaikan semua "pengecualian Beban Malas". Jika Anda membutuhkan sesuatu yang lebih canggih untuk penyetelan kinerja, menurut saya profil pengambilan adalah cara yang tepat.

Augusto
sumber
1
Saya kira Anda ingin mengatakan: Sebuah antipattern sangat umum ... . Meskipun saya setuju dengan membuka TX pada tingkat layanan, tetapi penggunaan OSIVini masih antipattern dan menyebabkan masalah yang sangat serius seperti ketidakmampuan untuk menangani pengecualian atau penurunan kinerja dengan baik. Singkatnya: IMHO OSIV adalah solusi yang santai, tetapi hanya bagus untuk proyek mainan.
G. Demecki