Чому відкрита сесія в режимі глибокого сну вважається поганою практикою?


108

І які альтернативні стратегії ви використовуєте, щоб уникнути LazyLoadExceptions?

Я розумію, що відкрита сесія має на увазі проблеми з:

  • Багатошарові програми, що працюють у різних jvm-кодах
  • Операції здійснюються лише в кінці, і, швидше за все, ви хотіли б отримати результати раніше.

Але, якщо ви знаєте, що ваша програма працює на одному vm, чому б не полегшити свій біль, використовуючи відкриту сесію в стратегії перегляду?


12
Чи вважається OSIV поганою практикою? Ким?
Йоханнес Бродволл

4
І - які хороші альтернативи?
Девід Рабіновіц

7
Цей мир тексту, якщо розробники швів: Є кілька проблем з цією реалізацією, найсерйозніша полягає в тому, що ми ніколи не можемо бути впевнені, що транзакція буде успішною, поки ми не здійснимо її, але до моменту вчинення транзакції "відкритого сеансу для перегляду", подання повністю винесено, і надана відповідь, можливо, вже була передана клієнтові. Як ми можемо повідомити користувача про те, що транзакція не вдалася?
дарпет


2
Про плюси та мінуси дивіться цю публікацію у блозі та власний досвід щодо неї - blog.jhades.org/open-session-in-view-pattern-pros-and-cons
Angular University

Відповіді:


46

Оскільки надсилання можливо неініціалізованих проксі, особливо колекцій, у шар перегляду та спричинення завантаження зі сплячого режиму звідти може викликати занепокоєння як з точки зору продуктивності, так і з розуміння.

Розуміння :

Використання OSIV "забруднює" шар перегляду занепокоєнням, пов'язаним із рівнем доступу до даних.

Шар перегляду не готується до обробки, HibernateExceptionщо може статися при ледачому завантаженні, але, мабуть, рівень доступу до даних є.

Продуктивність :

OSIV, як правило, перетягує належне завантаження під килим - ви, як правило, не помічаєте, що ваші колекції чи об'єкти ліниво ініціалізовані (можливо, N + 1). Більше зручності, менше контролю.


Оновлення: див . Антипаттерн OpenSessionInView для більшого обговорення цієї теми. Автор перелічує три важливі моменти:

  1. кожна лінива ініціалізація отримає вам запит, тобто кожному суб'єкту знадобиться N + 1 запитів, де N - кількість лінивих асоціацій. Якщо на екрані представлені табличні дані, читання журналу сплячки - це великий натяк на те, що ви не робите так, як слід
  2. це повністю перемагає шарувату архітектуру, оскільки ви цокотіть свої нігті з БД в шарі презентації. Це концептуальна проблема, тому я міг би жити з цим, але є наслідком
  3. нарешті, але не менш важливо, якщо виняток виникає під час отримання сеансу, це станеться під час написання сторінки: ви не можете представити користувачеві чисту сторінку помилок, і єдине, що ви можете зробити, це написати повідомлення про помилку в тілі

13
Гаразд, це "забруднює" шар перегляду за винятком сплячого режиму. Щодо продуктивності, я думаю, що проблема зовсім схожа, ніж отримати доступ до рівня обслуговування, який поверне ваш dto. Якщо ви зіткнулися з проблемою продуктивності, то вам слід оптимізувати цю конкретну проблему за допомогою розумнішого запиту або більш легкого dto. Якщо вам доведеться розробити занадто багато методів обслуговування для обробки можливостей, які вам можуть знадобитися, ви також «забруднюєте» рівень обслуговування. немає?
HeDinges

1
Одна відмінність полягає в тому, що це затримує закриття сесії сплячого режиму. Ви будете чекати, коли JSP буде наданий / записаний / тощо, і це довше зберігатиме об'єкти в пам'яті. Це може бути проблемою, особливо якщо вам потрібно записати дані про фіксацію сеансу.
Роберт Мунтяну

8
Немає сенсу говорити, що OSIV шкодить продуктивності. Які альтернативи існують, крім використання DTO? У такому випадку у вас завжди буде нижча продуктивність, оскільки дані, які використовуються будь-яким представленням даних, доведеться завантажувати навіть для представлень, які не потребують цього.
Йоганнес Бродволл

11
Я думаю, що забруднення працює навпаки. Якщо мені потрібно прагнути завантажувати дані, логічний рівень (або ще гірше, рівень доступу до даних) повинен знати, яким способом буде відображатися об'єкт. Змініть подання, і ви закінчите завантажувати потрібні вам речі або відсутні об'єкти, які вам потрібні. Виняток зі сплячки - це помилка і настільки ж отруєння, як і будь-який інший несподіваний виняток. Але продуктивність - це проблема. Питання щодо продуктивності та масштабованості змусять вас більше думати і працювати в рівні доступу до даних, і, можливо, змусять сесію закрити раніше
Jens Schauder

1
@JensSchauder "Змініть вигляд, і ви закінчите завантажувати речі, які вам не потрібні, або відсутні об'єкти, які вам потрібні". Це саме воно. Якщо ви зміните вигляд, набагато краще завантажувати речі, які вам не потрібні (так як ви, швидше за все, прагнете їх отримати) або з'ясувати відсутні об'єкти, як ви отримаєте виняток "Ледаче завантаження", ніж дозволити завантажувати перегляд це ліниво, оскільки це призведе до проблеми N + 1, і ви навіть не знаєте, що це відбувається. Тож ІМО його краще обслуговує рівень (і ви) знаєте, що він надсилається, ніж огляд, який ледаче завантажується, і ви нічого про це не знаєте.
Єшурун

40

Для більш детального опису ви можете прочитати мою статтю " Відкрита сесія" у "Перегляд протизразка" . В іншому випадку, ось короткий опис того, чому не слід використовувати Open Session In View.

Відкрита сесія в режимі перегляду погано підходить до отримання даних. Замість того, щоб дозволити бізнес-шару вирішувати, як найкраще отримати всі асоціації, необхідні шару «Перегляд», він змушує Контекст стійкості залишатися відкритим, щоб шар Перегляд може спровокувати ініціалізацію проксі.

введіть тут опис зображення

  • OpenSessionInViewFilterВикликає openSessionметод базового активу SessionFactoryі отримує новий Session.
  • SessionПов'язаний з TransactionSynchronizationManager.
  • OpenSessionInViewFilterНазиває doFilterв якості javax.servlet.FilterChainпосилання об'єкта і запит додатково обробляється
  • DispatcherServletНазивається, і він направляє запит HTTP , щоб лежачий в основі PostController.
  • У PostControllerдзвінки на , PostServiceщоб отримати список Postсутностей.
  • PostServiceВідкриває нову транзакцію, і HibernateTransactionManagerповторно той же , Sessionщо був відкритий OpenSessionInViewFilter.
  • PostDAOОтримує список Postосіб без ініціалізації будь ледачою асоціації.
  • PostServiceЗдійснює основну угоду, але Sessionне закритий , так як він був відкритий зовні.
  • У DispatcherServletзапуску рендеринга інтерфейсу, який, в свою чергу, переходить ледачі асоціації і викликає їх ініціалізації.
  • Can OpenSessionInViewFilterможе закрити Session, і базове з'єднання бази даних також вивільнено.

На перший погляд, це може не виглядати як жахлива річ, але, коли ви переглядаєте це з точки зору бази даних, ряд недоліків починає ставати більш очевидним.

Сервісний рівень відкривається та закриває транзакцію бази даних, але після цього явної транзакції не відбувається. З цієї причини кожна додаткова заява, видана на етапі візуалізації користувальницького інтерфейсу, виконується в режимі автоматичної фіксації. Функція автоматичного фіксації чинить тиск на сервер бази даних, оскільки кожне твердження повинно передати журнал транзакцій на диск, тому спричиняючи багато трафіку вводу / виводу на стороні бази даних. Однією з оптимізацій було б позначити Connectionяк лише для читання, що дозволить серверу баз даних уникати запису в журнал транзакцій.

Більше проблем не існує, оскільки заяви створюються як службовим рівнем, так і процесом надання інтерфейсу користувача. Для написання тестів на інтеграцію, що підтверджують кількість генерованих висловлювань, потрібно пройти всі шари (веб, сервіс, DAO), при цьому додаток буде розміщено у веб-контейнері. Навіть при використанні бази даних в пам'яті (наприклад, HSQLDB) та легкого веб-сервера (наприклад, Jetty), ці інтеграційні тести виконуватимуться повільніше, ніж якщо б шари були розділені і тести інтеграції заднім числом використовували базу даних, тоді як фронтальні тести інтеграції взагалі глузували з сервісного рівня.

Шар інтерфейсу обмежений навігаційними асоціаціями, які, в свою чергу, можуть викликати проблеми запитів N + 1. Незважаючи на те, що Hibernate пропонує @BatchSizeпакетні асоціації для отримання пакетів і FetchMode.SUBSELECTдля вирішення цього сценарію, примітки впливають на план вибору за замовчуванням, тому вони застосовуються до кожного випадку використання бізнесу. З цієї причини запит рівня доступу до даних набагато підходить, оскільки він може бути налаштований під поточні вимоги використання даних щодо поточного використання.

І останнє, але не менш важливе, підключення до бази даних може відбуватися протягом усієї фази візуалізації інтерфейсу (залежно від режиму випуску з'єднання), що збільшує час оренди з'єднання та обмежує загальну пропускну здатність транзакцій через перевантаженість пулу підключення до бази даних. Чим більше з'єднання проведено, тим більше інших одночасних запитів буде чекати, щоб отримати з'єднання з пулу.

Таким чином, або ви тримаєте з'єднання занадто довго, або ви отримуєте / випускаєте кілька з'єднань для одного HTTP-запиту, тому тиснуть на базовий пул з'єднань і обмежуючи масштабованість.

Весняний черевик

На жаль, Open Session in View увімкнено у Spring Boot за замовчуванням .

Отже, переконайтеся, що у application.propertiesфайлі конфігурації є такий запис:

spring.jpa.open-in-view=false

Це відключить OSIV, щоб ви могли правильно впоратисяLazyInitializationException .


3
Використання Open Session in View з автоматичним фіксацією можливо, але не так, як це було призначено розробниками Hibernate. Тож, хоча у відкритої сесії у View є свої недоліки, автоматична фіксація не одна, тому що ви можете просто вимкнути її та все ж використовувати.
stefan.m

Ви говорите про те, що відбувається всередині транзакції, і це правда. Але фаза візуалізації веб-шару відбувається за межами сплячого режиму, отже, ви отримуєте режим автокомісії. Має сенс?
Влад Міхалча

Я думаю, що це варіант, який не є оптимальним для Open Session in View. Сеанс і транзакція повинні залишатися відкритими, доки подання не буде надано, тоді немає необхідності в режимі автокомісії.
stefan.m

2
Сесія залишається відкритою. Але транзакція цього не робить. Продовження транзакції протягом усього процесу не є оптимальним, оскільки збільшує його тривалість, а замки тримаються довше, ніж потрібно. Уявіть, що станеться, якщо представлення видає RuntimeException. Чи буде відкат транзакції, оскільки надання інтерфейсу не вдалося?
Влад Михальча

Дуже дякую за дуже детальну відповідь! Я змінив би лише керівництво в кінці кінців, оскільки користувачі весняного завантаження, ймовірно, не будуть використовувати jpa таким чином.
Skeeve

24
  • транзакції можуть здійснюватися на рівні обслуговування - транзакції не пов'язані з OSIV. Це те, Sessionщо залишається відкритим, а не транзакція.

  • якщо шари вашої програми поширюються на декілька машин, ви майже не можете використовувати OSIV - вам доведеться ініціалізувати все необхідне, перш ніж надсилати об’єкт по дроту.

  • OSIV - це приємний і прозорий (тобто ніхто з вашого коду не знає, що це відбувається) спосіб використовувати переваги продуктивності від ледачого завантаження


2
Щодо першої точки кулі, це, принаймні, не вірно для оригінального OSIV з вікі JBoss, воно також обробляє розмежування транзакцій навколо запиту.
Паскаль Thivent

@PascalThivent Яка частина вас змусила так подумати?
Sanghyun Lee

13

Я б не сказав, що відкрита сесія в перегляді вважається поганою практикою; що справляє таке враження?

Open-Session-In-View - це простий підхід до обробки сеансів роботи зі сплячим режимом. Оскільки це просто, іноді спрощено. Якщо вам потрібен тонкий контроль над транзакціями, наприклад, кілька запитів у запиті, Open-Session-In-View - це не завжди хороший підхід.

Як зазначали інші, існують певні компроміси в OSIV - ви набагато більше схильні до проблеми N + 1, оскільки ви менше шансів усвідомити, які транзакції ви починаєте. У той же час це означає, що вам не потрібно змінювати рівень обслуговування, щоб адаптуватися до незначних змін у вашому представленні даних.


5

Якщо ви використовуєте контейнер Інверсії управління (IoC), такий як Spring, вам, можливо, захочеться ознайомитись із визначенням квасолі . По суті, я кажу Spring, щоб він дав мені сплячий Sessionоб'єкт, життєвий цикл якого охоплює весь запит (тобто він створюється та знищується на початку та в кінці запиту HTTP). Мені не потрібно хвилюватися з приводу LazyLoadExceptionзакриття сесії, оскільки контейнер IoC управляє цим для мене.

Як уже згадувалося, вам доведеться думати про проблеми з продуктивністю N + 1 SELECT. Ви завжди можете налаштувати свою сплячу особу після цього, щоб не хочуть приєднуватися до завантаження в місцях, де продуктивність є проблемою.

Рішення для збору квасолі не є специфічним для весни. Я знаю, що PicoContainer пропонує ті ж можливості, і я впевнений, що інші зрілі контейнери IoC пропонують щось подібне.


1
Чи є у вас вказівник на те, що фактична реалізація сеансів сплячого режиму стає доступною для перегляду за допомогою замовлених бобів?
Марво

4

На мій власний досвід, OSIV не такий вже й поганий. Єдине домовленість, яку я зробив, - це використання двох різних транзакцій: - перша, відкрита в "сервісному шарі", де я маю "бізнес-логіку", - друга відкрита перед відображенням перегляду


3

Я щойно написав повідомлення про деякі вказівки щодо використання відкритого сеансу для перегляду в своєму блозі. Перевірте це, якщо цікавитесь.

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


1
Як правило, правило, якщо ви даєте відповідь, краще зробити більше, ніж просто посилання в іншому місці. Можливо, наведіть одне-два речення або перелічені пункти, що надають суть. Добре зв’язуватися, але ви хочете надати трохи додаткової цінності. В іншому випадку ви можете просто прокоментувати і розмістити посилання там.
DWright

посилання у цій відповіді варто прочитати, воно дає хороші вказівки щодо того, коли використовувати OSIV, а ні
ams

1

Я проти іржавого режиму .. але я думаю, що за один сеанс сплячки можливо зробити кілька транзакцій. Тож межі транзакцій не повинні бути такими ж, як події початку / зупинки сесії.

OSIV, imo, в першу чергу корисний, оскільки ми можемо уникати написання коду для запуску "контексту постійності" (він же сеанс) кожного разу, коли запит потребує доступу до БД.

У вашому сервісному шарі, ймовірно, вам потрібно буде здійснювати дзвінки до методів, які мають різні потреби в транзакціях, таких як "Необхідно, Нове Потрібно тощо". Єдине, що потрібно цим методам, це те, що хтось (тобто фільтра OSIV) запустив контекст постійності, так що єдине, про що вони повинні турбуватися, - це "дайте мені сплячий сеанс для цього потоку. Мені потрібно зробити кілька БД ".


1

Це не допоможе занадто багато, але ви можете перевірити мою тему тут: * Hibernate Cache1 OutOfMemory з OpenSessionInView

У мене є деякі проблеми OutOfMemory через OpenSessionInView та багато завантажених організацій, оскільки вони залишаються в рівні кешу Hibernate1 і не збирають сміття (я завантажую багато об'єктів з 500 елементами на сторінку, але всі об'єкти залишаються в кеші)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.