Дизайн приложения с отложенной загрузкой в ​​Hibernate

87

Я обычно использую Hibernate в сочетании со средой Spring и его возможностями декларативного разграничения транзакций (например, @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/…
Adrien Be
Не могли бы вы привести пример для варианта 4?
degreeightdc 08

Ответы:

26

Как все мы знаем, спящий режим старается быть максимально неинвазивным и прозрачным.

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

Обратите внимание, что Hibernate не может читать мысли, поэтому, если вы знаете, что вам нужен определенный набор зависимостей для конкретной операции, вам нужно как-то выразить свои намерения в Hibernate.

С этой точки зрения решения, которые явно выражают эти намерения (а именно, 2, 4 и 7), выглядят разумными и не страдают отсутствием прозрачности.

axtavt
источник
Вы правы, конечно, максимальная прозрачность работает только пока. Это хороший выбор, на который вы пошли.
Johan Sjöberg
ИМХО: совершенно правильный ответ. Действительно, это миф. Кстати: я бы проголосовал за варианты 4 и 7 (или вообще отказался от ORM)
Г. Демеки
7

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

  • объект fooзагружается и помещается на карту;
  • другой поток берет этот объект с карты и вызывает foo.getBar()(что-то, что никогда раньше не вызывалось и вычисляется лениво);
  • бум!

Итак, чтобы решить эту проблему, у нас есть ряд правил:

  • как можно более прозрачно OpenSessionInViewFilterпереносить сеансы (например, для веб-приложений);
  • иметь общий API для потоков / пулов потоков, где привязка / отмена привязки сеанса базы данных выполняется где-то на верхнем уровне иерархии (в оболочке try/finally), поэтому подклассам не нужно думать об этом;
  • при передаче объектов между потоками передавайте идентификаторы вместо самих объектов. Принимающий поток может загрузить объект, если это необходимо;
  • при кэшировании объектов никогда не кэшируйте объекты, кроме их идентификаторов. Имейте абстрактный метод в вашем DAO или классе менеджера для загрузки объекта из кеша Hibernate 2-го уровня, когда вы знаете идентификатор. Стоимость извлечения объектов из кеша Hibernate 2-го уровня по-прежнему намного дешевле, чем переход в БД.

Как видите, это действительно далеко от неинвазивного и прозрачного . Но цена по-прежнему терпима по сравнению с ценой, которую мне пришлось бы заплатить за нетерпеливую загрузку. Проблема с последним состоит в том, что иногда это приводит к эффекту бабочки при загрузке одного объекта, на который имеется ссылка, не говоря уже о коллекции сущностей. Потребление памяти, загрузка процессора и задержка, если не упомянуть, также намного хуже, так что я думаю, что смогу с этим жить.

Mindas
источник
Спасибо за ваш ответ. Потеря transparencyзаключается в том, что приложение заставляет заботиться о загрузке ленивых объектов. Если бы все было извлечено с нетерпением, приложение могло бы не знать, сохраняются ли объекты в базе данных или нет, поскольку Foo.getBar()всегда будет успешным. > when passing objects between threads, pass IDsДа, это соответствовало бы №5.
Johan Sjöberg
3

Очень распространенный шаблон - использовать OpenEntityManagerInViewFilter, если вы создаете веб-приложение.

Если вы создаете службу, я бы открыл TX в общедоступном методе службы, а не в DAO, поскольку очень часто метод требует получения или обновления нескольких сущностей.

Это устранит любое «исключение отложенной загрузки». Если вам нужно что-то более продвинутое для настройки производительности, я думаю, что профили выборки - это то, что вам нужно.

Аугусто
источник
1
Я предполагаю , что вы хотели сказать: очень распространенный антипаттерн ... . Хотя я бы согласился с открытием TX на уровне обслуживания, но использование шаблона по- OSIVпрежнему является антипаттерном и приводит к очень серьезным проблемам, таким как неспособность корректно обрабатывать исключения или снижение производительности. Подводя итог: IMHO OSIV - простое решение, но годное только для игрушечных проектов.
G. Demecki