Решить проблему Liby-Hibernate с помощью hibernate.enable_lazy_load_no_trans

Я страдаю от позорного исключения в спящем режиме

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

Теперь сообщество приветствует

<property name="hibernate.enable_lazy_load_no_trans" value="true"/>

заявив, что он решает проблему, но ИСПОЛЬЗУЙТЕ ЭТО С ПРЕДОСТЕРЕЖЕНИЕМ.

Что они подразумевают под этим с осторожностью? Что на самом деле это свойство?

Пожалуйста, дайте мне какие-нибудь идеи. Спасибо заранее.

Ответ 1

Проблема с этим подходом заключается в том, что вы можете иметь эффект N + 1.

Представьте, что у вас есть следующее лицо:

public class Person{
    @OneToMany // default to lazy
    private List<Order> orderList;
}

Если у вас есть отчет, который возвращает 10K лиц, и если в этом отчете вы выполните код person.getOrderList() JPA/Hibernate выполнит 10K запросов. Это эффект N + 1, вы не будете контролировать все запросы, которые будут выполнены.

Представьте себе, что порядок выглядит следующим образом:

public class Order{
    @OneToMany // default to lazy
    private List<EmailSent> emailSentList;
}

Представьте себе, что у вас есть итерация с person.getOrderList() и для каждого Order order вы будете делать order.getEmailSentList(). Вы видите проблему сейчас?

Для LazyInitializationException у вас могут быть некоторые решения:

  • Используйте подход OpenInSessionInView. Вам нужно будет создать WebFilter, который откроет и закроет транзакцию. Проблема заключается в эффекте N + 1.
  • Используйте конфигурацию hibernate.enable_lazy_load_no_trans, которая представляет собой спящий режим, и вы не сможете перенести свой проект на другого поставщика JPA, если это необходимо. Вы также можете получить эффект N + 1.
  • Используйте функцию EJB с именем PersistenceContext Extended. С этим вы сохраните контекст, открытый несколькими транзакциями. Проблемы: эффект N + 1 может произойти, использовать много серверной памяти (объекты останутся управляемыми)
  • Используйте FETCH в запросе. С помощью этого подхода вы можете сделать JPQL/HQL, например: select p from Person p join fetch p.orderList. С помощью этого запроса у вас будет загружен ваш список из базы данных и не будет иметь эффекта N + 1. Проблема в том, что вам нужно будет написать JPQL для каждого случая.

Если у вас все еще есть проблемы, проверьте эти ссылки:

Ответ 2

Это противоречит тому, как мы можем использовать Hibernate для обеспечения повторяемости семантики чтения с концепцией Session. Когда объект сначала загружается и если объект ссылается снова в течение срока действия сеанса, то тот же самый объект возвращается IRRESPECTIVE того, был ли этот объект изменен в БД. Это повторяемая семантика чтения, автоматически предоставляемая спящим режимом.

С этим параметром у вас нет сеанса, предоставляющего эту гарантию, поэтому, если вы теперь получите доступ к этим данным, вы получите самую последнюю версию данных.

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

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

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

Ответ 3

Лучшим способом решения LazyInitializationException является использование директивы JOIN FETCH в ваших запросах сущности.

Загрузка EAGER плохо для производительности. Кроме того, существуют анти-шаблоны, такие как:

Которые вы никогда не должны использовать, поскольку они либо требуют, чтобы соединение с базой данных было открыто для визуализации пользовательского интерфейса (Open Session in View), либо соединение с базой данных необходимо для каждой ленивой ассоциации, которая извлекается за пределы исходного контекста сохранения (hibernate.enable_lazy_load_no_trans),

Иногда вам даже не нужны сущности, а проекция DTO еще лучше.

Ответ 4

Возможно, потому что есть лучшие решения, такие как @Transactional, где открытие и закрытие сеансов следует очень частому шаблону "открыть сеанс, а затем завернуть все в try-catch-finally, catch откатывается и, наконец, закрывает сеанс". Эта аннотация обычно находится на уровне запросов для веб-приложений и служб.

Или, если вам нужно более подробное управление, вы можете открывать сеансы вручную с помощью SessionFactory.

И, как говорили другие, ленивая загрузка - это то, о чем вам нужно знать. Это не серебряная пуля, но это может быть очень полезно. Как правило, если ваши приложения спроектированы так, чтобы иметь множество небольших запросов, тогда это нормально.

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

Или вы можете просто забыть все это сейчас. Используйте ленивую загрузку, пока она не станет проблемой. И если это произойдет, вы все равно были бы лучше с Мибатисом.