java - لماذا يعتبر "جلسة إسبات مفتوحة" في "عرض" ممارسة سيئة؟




hibernate jpa (6)

وأي نوع من الاستراتيجيات البديلة التي تستخدمها لتجنب LazyLoadExceptions؟

أفهم أن الجلسة المفتوحة في العرض لها مشكلات مع:

  • تشغيل التطبيقات ذات الطبقات في jvm مختلفة
  • يتم تنفيذ المعاملات في النهاية فقط ، وعلى الأرجح ترغب في الحصول على النتائج من قبل.

ولكن ، إذا كنت تعرف أن تطبيقك يعمل على جهاز واحد ، فلم لا تخفف من آلامك عن طريق استخدام استراتيجية مفتوحة في العرض؟


أنا ضد صدئ في سبات .. ولكن أعتقد أنه من الممكن أن يكون هناك معاملات متعددة في جلسة إسبرس واحدة. لذا ، لا يجب أن تكون حدود معاملتك مماثلة لأحداث بدء / إيقاف الجلسة.

OSIV ، imo ، في المقام الأول مفيد لأنه يمكننا تجنب كتابة التعليمات البرمجية لبدء "سياق المثابرة" (aka aka session) في كل مرة يحتاج فيها الطلب إلى الوصول إلى DB.

في طبقة الخدمة ، ستحتاج على الأرجح إلى إجراء مكالمات لطرق لها احتياجات معاملات مختلفة ، مثل "مطلوب ، مطلوب جديد ، إلخ". الشيء الوحيد الذي تحتاجه هذه الأساليب هو أن شخص ما (أي مرشح OSIV) قد بدأ في سياق المثابرة ، بحيث يكون الشيء الوحيد الذي يجب أن يقلق عليه هو "" أعطني جلسة السبات لهذا الموضوع .. أحتاج إلى القيام ببعض الاشياء DB ".


إذا كنت تستخدم حاوية انعكاس التحكم (IoC) مثل Spring ، فقد ترغب في القراءة في مجال تحليل الفول . بشكل أساسي ، أطلب من Spring أن يعطيني كائن Session Hibernate Session حياته يمتد الطلب بأكمله (على سبيل المثال ، يتم إنشاؤه وتدميره في بداية طلب HTTP ونهايته). لا داعي للقلق بشأن LazyLoadException أو إغلاق الجلسة لأن إدارة IoC تدير ذلك بالنسبة لي.

كما ذكر ، سيكون عليك التفكير في مشاكل أداء N + 1 SELECT. يمكنك دائمًا تهيئة كيان Hibernate الخاص بك بعد ذلك للقيام بالتحميل المشترك في الأماكن التي يكون فيها الأداء مشكلة.

حل تحديد النطاق غير محدد في الربيع. وأنا أعلم أن PicoContainer يقدم نفس القدرة وأنا متأكد من أن حاويات IoC الأخرى الناضجة تقدم شيئاً مماثلاً.



للحصول على وصف أطول ، يمكنك قراءة مقالتي Open Session In View Anti-Pattern . بخلاف ذلك ، إليك ملخصًا عن سبب عدم استخدام Open Session In View.

يتخذ Open Session In View منهجًا سيئًا لجلب البيانات. وبدلاً من السماح لطبقة الأعمال بتحديد أفضل طريقة لجلب جميع الارتباطات التي تحتاج إليها طبقة العرض ، فإنها تفرض بقاء سياق الاستمرار ليظل مفتوحًا حتى تتمكن طبقة العرض من تشغيل التهيئة Proxy.

  • OpenSessionInViewFilter يستدعي أسلوب openSession من openSession الأساسي ويحصل على Session جديدة.
  • تكون Session مرتبطة بـ TransactionSynchronizationManager .
  • OpenSessionInViewFilter يستدعي OpenSessionInViewFilter مرجع كائن javax.servlet.FilterChain ويتم معالجة الطلب مرة أخرى
  • يتم استدعاء DispatcherServlet ويقوم بتوجيه طلب HTTP إلى PostController الأساسي.
  • يستدعي PostService للحصول على قائمة PostService Post .
  • يفتح PostService معاملة جديدة و HibernateTransactionManager reuses نفس Session التي تم فتحها بواسطة OpenSessionInViewFilter .
  • تجلب PostDAO قائمة الكيانات Post بدون إنشاء أي ارتباط كسول.
  • تلزم PostService المعاملة الأساسية ، لكن Session ليست مغلقة نظرًا لأنه تم فتحها خارجيًا.
  • يبدأ DispatcherServlet جعل واجهة المستخدم ، والتي بدورها ، تنقل الارتباطات البطيئة وتطلق التهيئة الخاصة بها.
  • يمكن لـ OpenSessionInViewFilter إغلاق Session ، ويتم تحرير اتصال قاعدة البيانات الأساسي أيضًا.

من النظرة الأولى ، قد لا يبدو هذا شيئًا فظيعًا ، ولكن بمجرد أن تشاهده من منظور قاعدة البيانات ، تبدأ سلسلة من العيوب في أن تصبح أكثر وضوحًا.

تفتح طبقة الخدمة وتغلق معاملة قاعدة البيانات ، ولكن بعد ذلك ، لا توجد أي معاملة صريحة جارية. لهذا السبب ، يتم تنفيذ كل بيان إضافي صادر من مرحلة تقديم واجهة المستخدم في وضع الالتزام التلقائي. يضع التزام تلقائي الضغط على خادم قاعدة البيانات لأن كل بيان يجب أن يسحب سجل المعاملات إلى القرص ، وبالتالي يتسبب في الكثير من حركة مرور I / O على جانب قاعدة البيانات. قد يكون أحد التحسينات هو وضع علامة على Connection كقراءة فقط مما يسمح لخادم قاعدة البيانات بتجنب الكتابة إلى سجل المعاملة.

لم يعد هناك فصل للمخاوف لأنه يتم إنشاء عبارات لكل من طبقة الخدمة وعملية تقديم واجهة المستخدم. تتطلب اختبارات تكامل الكتابة التي تؤكد عدد العبارات التي يتم توليدها المرور عبر كل الطبقات (الويب ، الخدمة ، DAO) ، مع نشر التطبيق على حاوية ويب. حتى عند استخدام قاعدة بيانات في الذاكرة (على سبيل المثال HSQLDB) وخادم ويب خفيف الوزن (مثل Jetty) ، فإن اختبارات التكامل هذه ستكون أبطأ في التنفيذ مما لو تم فصل الطبقات واستخدمت اختبارات التكامل الخلفي قاعدة البيانات ، بينما كانت اختبارات التكامل الأمامية تسخر من طبقة الخدمة تمامًا.

تقتصر طبقة واجهة المستخدم على التنقل بين الجمعيات والتي يمكنها بدورها تشغيل مشكلات طلب البحث N + 1. على الرغم من أن Hibernate تقدم @BatchSize لجلب الارتباطات في مجموعات ، و FetchMode.SUBSELECT للتعامل مع هذا السيناريو ، تؤثر التعليقات التوضيحية على خطة الجلب الافتراضية ، بحيث يتم تطبيقها على كل حالة استخدام الأعمال. لهذا السبب ، يكون استعلام طبقة الوصول إلى البيانات أكثر ملاءمة لأنه يمكن تخصيصه لمتطلبات جلب بيانات حالة الاستخدام الحالية.

أخيرًا وليس آخرًا ، يمكن عقد اتصال قاعدة البيانات طوال مرحلة تقديم واجهة المستخدم (اعتمادًا على وضع تحرير الاتصال) مما يزيد من وقت تأجير الاتصال ويحد من إجمالي معدل المعاملات بسبب الازدحام في مجموعة اتصال قاعدة البيانات. كلما تم تعليق الاتصال ، سيتم انتظار طلبات متزامنة أخرى للحصول على اتصال من التجمع.

لذلك ، إما أن تحصل على الاتصال لفترة طويلة ، إما أن تقوم بإتاحة / تحرير اتصالات متعددة لطلب HTTP واحد ، وبالتالي الضغط على تجمع الاتصال الأساسي والحد من قابلية التوسع.

التمهيد الربيع

للأسف ، يتم تمكين جلسة مفتوحة في العرض بشكل افتراضي في Spring Boot .

لذا ، تأكد من وجود ملف الإدخال التالي في ملف التهيئة application.properties :

spring.jpa.open-in-view=false

سيؤدي ذلك إلى تعطيل OSIV ، بحيث يمكنك التعامل مع LazyInitializationException بالطريقة الصحيحة .


نظرًا لأن إرسال بروكسيات ربما غير مهيأة ، خاصةً المجموعات ، في طبقة العرض وتحريك التحميل في السبات من هناك ، قد يكون مصدرًا للقلق من وجهة نظر الأداء والتفهم.

فهم :

يؤدي استخدام OSIV إلى "تلويث" طبقة العرض مع المخاوف المتعلقة بطبقة الوصول إلى البيانات.

لا يتم تحضير طبقة العرض للتعامل مع HibernateException والتي قد تحدث عند التحميل البطيء ، ولكن يفترض أن طبقة الوصول إلى البيانات هي.

الأداء :

يميل OSIV إلى محاولة تحميل الكيان الصحيح تحت السجادة - تميل إلى عدم ملاحظة أن مجموعاتك أو كياناتك قد بدأت بشكل مسرع (ربما N + 1). مزيد من الراحة ، أقل السيطرة.

تحديث: راجع مضاد الصدع OpenSessionInView لإجراء مناقشة أكبر بخصوص هذا الموضوع. يسرد المؤلف ثلاث نقاط مهمة:

  1. ستحصل كل عملية تهيئة كسولة على استعلام يعني أن كل كيان يحتاج إلى استعلامات N + 1 ، حيث N هو عدد الارتباطات البطيئة. إذا كانت الشاشة تعرض بيانات مجدولة ، فإن قراءة سجل Hibernate هي تلميح كبير بأنك لا تفعل كما ينبغي
  2. هذا يهزم بنية الطبقات بالكامل ، لأنك تلطخ أظافرك مع DB في طبقة التقديم. هذا خداع مفاهيمي ، لذلك يمكنني العيش معه ولكن هناك نتيجة طبيعية
  3. أخيرًا وليس آخرًا ، إذا حدث استثناء أثناء جلب الجلسة ، فسيحدث أثناء كتابة الصفحة: لا يمكنك تقديم صفحة خطأ نظيفة للمستخدم ، والشيء الوحيد الذي يمكنك فعله هو كتابة رسالة خطأ في النص

هذا لن يساعد كثيرا ولكن يمكنك التحقق من موضوعي هنا: * السبات Cache1 OutOfMemory مع OpenSessionInView

لدي بعض مشكلات OutOfMemory بسبب OpenSessionInView والكثير من الكيانات التي تم تحميلها ، لأنهم يظلون في المستوى 1 من ذاكرة التخزين المؤقت ولا يتم تجميع البيانات المهملة (يتم تحميل الكثير من الكيانات التي تحتوي على 500 عنصر في كل صفحة ، ولكن تبقى جميع الكيانات في ذاكرة التخزين المؤقت)





open-session-in-view