Сплячий режим додатків із ледачим навантаженням


87

Я схильний використовувати Hibernate у поєднанні з Spring framework та його декларативними можливостями розмежування транзакцій (наприклад, @Transactional ).

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


Я бачу ряд дизайнерських альтернатив з різним рівнем прозорості.

  1. Зробіть стосунки не ледачими (наприклад, fetchType=FetchType.EAGER)
    • Це порушує всю ідею ледачого завантаження ..
  2. Ініціалізуйте колекції за допомогою Hibernate.initialize(proxyObj);
    • Це передбачає відносно високу зв'язок з DAO
    • Хоча ми можемо визначити інтерфейс з initialize, інші реалізації не гарантують надання будь-якого еквівалента.
  3. Додайте поведінку транзакцій до самих стійких Modelоб’єктів (використовуючи динамічний проксі-сервер або @Transactional)
    • Я не пробував підхід динамічного проксі-сервера, хоча ніколи не здавався, щоб @Transactional працював над самими стійкими об'єктами. Можливо, завдяки цьому сплячому режиму є операція над проксі-сервером, з яким слід працювати.
    • Втрата контролю, коли операції фактично відбуваються
  4. Надайте як ледачий, так і не ледачий API, наприклад, loadData()іloadDataWithDeps()
    • Змушує програму знати, коли застосовувати яку рутину, знову щільне зчеплення
    • Метод переповнення,, loadDataWithA()....,loadDataWithX()
  5. Примусово шукати залежності, наприклад, лише забезпечуючи byId()операції
    • Потрібно багато необ'єктно-орієнтованих процедур, наприклад,, findZzzById(zid)а потім getYyyIds(zid)замістьz.getY()
    • Може бути корисним отримання кожного об’єкта в колекції по одному, якщо між транзакціями є великі накладні витрати на обробку.
  6. Зробіть частину програми @Transactional замість лише DAO
    • Можливі міркування про вкладені транзакції
    • Потрібні процедури, адаптовані для управління транзакціями (наприклад, досить малі)
    • Невеликий програмний вплив, хоча може призвести до великих операцій
  7. Надайте DAO динамічні профілі отримання , наприклад,loadData(id, fetchProfile);
    • Програми повинні знати, який профіль використовувати коли
  8. Тип транзакцій AoP, наприклад, операції перехоплення та виконання транзакцій за необхідності
    • Потрібна маніпуляція з байт-кодом або використання проксі
    • Втрата контролю під час здійснення транзакцій
    • Чорна магія, як завжди :)

Я пропустив якийсь варіант?


Який ваш найкращий підхід при спробі мінімізувати вплив lazy-loadedвзаємозв’язків у дизайні вашого додатка?

(О, і вибачте за WoT )


приклад для варіанту 2 і 5: m-hewedy.blogspot.ch/2010/03/…
Адріен Будь

Не могли б ви навести приклад для варіанту 4?
degreesightdc

Відповіді:


26

Як ми всі знаємо, сплячий режим намагається бути максимально неінвазивним і максимально прозорим

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

Зауважте, що Hibernate не може читати думки, тому, якщо ви знаєте, що вам потрібен певний набір залежностей для певної операції, вам потрібно якось висловити свої наміри Hibernate.

З цієї точки зору рішення, що прямо виражають ці наміри (а саме, 2, 4 та 7), виглядають обґрунтованими та не страждають від відсутності прозорості.


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

ІМХО: абсолютно правильна відповідь. Дійсно, це міф. До речі: мій голос був би за варіанти 4 і 7 (або взагалі відійти від
ОРМ

7

Я не впевнений, на яку проблему (спричинену лінивістю) ви натякаєте, але для мене найбільшим болем є уникнення втрати контексту сеансу у власних кешах додатків. Типовий випадок:

  • об'єкт fooзавантажується і поміщається на карту;
  • інший потік бере цей об’єкт з карти і викликає foo.getBar()(те, що ніколи раніше не викликалося і ліниво оцінюється);
  • бум!

Отже, для вирішення цього питання ми маємо ряд правил:

  • обгортати сесії якомога прозоріше (наприклад, OpenSessionInViewFilterдля веб-додатків);
  • мати загальний API для потоків / пулів потоків, де db session bind / unbind виконується десь високо в ієрархії (загорнуте в try/finally), тому підкласи не повинні про це думати;
  • передаючи об'єкти між потоками, передайте ідентифікатори замість самих об'єктів. Одержуючий потік може завантажити об'єкт, якщо це потрібно;
  • кешуючи об'єкти, ніколи не кешуйте об'єкти, але їх ідентифікатори. Майте абстрактний метод у своєму DAO або класі менеджера для завантаження об’єкта з кеш-пам’яті сплячого режиму 2-го рівня, коли ви знаєте ідентифікатор. Вартість отримання об’єктів із кеш-пам’яті Hibernate 2-го рівня все ще набагато дешевша, ніж перехід до БД.

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


Спасибі за Вашу відповідь. Втрата transparencyполягає у примушенні програми дбати про завантаження ледачих об’єктів. Якби все було охоче отримано, програма могла б зовсім не знати, зберігаються ці об'єкти у базі даних чи ні, оскільки Foo.getBar()це завжди вдасться. > when passing objects between threads, pass IDs, так, це відповідало б №5.
Йоган Шеберг

3

Дуже поширеним шаблоном є використання OpenEntityManagerInViewFilter, якщо ви створюєте веб-програму.

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

Це дозволить вирішити будь-які "винятки з ледачим навантаженням". Якщо вам потрібно щось більш просунуте для налаштування продуктивності, я думаю, що вибір профілів - це шлях.


1
Я припускаю , що ви хотіли сказати: дуже поширений антипаттерн ... . Хоча я б погодився з відкриттям TX на рівні сервісу, але використання його OSIVвсе ще є антипаттерном і призводить до дуже серйозних проблем, таких як неможливість витонченої роботи з винятками або погіршення продуктивності. Підводячи підсумок: IMHO OSIV - це легке рішення, але корисне лише для іграшкових проектів.
Г. Демецький,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.