Как решить LazyInitializationException при использовании JPA и Hibernate - PullRequest
49 голосов
/ 23 февраля 2009

Я работаю над проектом для клиента, который хочет использовать ленивую инициализацию. Они всегда получают «исключение отложенной инициализации» при отображении классов в режиме отложенной загрузки по умолчанию.

@JoinTable(name = "join_profilo_funzionalita", joinColumns = {@JoinColumn(name =    "profilo_id", referencedColumnName = "profilo_id")}, inverseJoinColumns = {@JoinColumn(name = "funzionalita_id", referencedColumnName = "funzionalita_id")})
//@ManyToMany(fetch=FetchType.EAGER) - no exceptions if uncommented
@ManyToMany 
private Collection<Funzionalita> funzionalitaIdCollection;

Существует ли стандартный шаблон, использующий классы JPA, чтобы избежать этой ошибки?

Фрагменты приветствуются, большое спасибо за ваше время.

Ответы [ 9 ]

60 голосов
/ 11 августа 2012

Hibernate 4.1.6 наконец-то решает эту проблему: https://hibernate.atlassian.net/browse/HHH-7457

Вам необходимо установить hibernate-свойство hibernate.enable_lazy_load_no_trans = true

Вот как это сделать весной:

<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="packagesToScan" value="com.mycompany.somepackage"/>
    <property name="jpaVendorAdapter" ref="hibernateVendorAdapter"/>
    <property name="jpaDialect" ref="jpaDialect"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.enable_lazy_load_no_trans">true</prop>
        </props>
    </property>
</bean>

Вуаля; Теперь вам не нужно беспокоиться об исключении LazyInitializationException при навигации по вашей доменной модели за пределами hibernate-сессии (постоянный контекст в «JPA-говорят»)

16 голосов
/ 10 мая 2009

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

  1. Позвоните соответствующему получателю. После того, как поле извлечено в бин, оно там после закрытия сессии.
  2. Вы можете инициализировать поле в запросе EJBQL, ищите ключевое слово JOIN FETCH.
  3. Включить AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS, если вы используете версию Hibernate, которая ее поддерживает.

При попытке решить эти проблемы может возникнуть несколько проблем:

  1. Вызов getters может быть оптимизирован компилятором JIT (иногда это занимает некоторое время).
  2. Сущности, которые вы пытаетесь JOIN FETCH, могут быть связаны множеством «многих» отношений, включающих списки. В этом случае результирующий запрос возвращает неоднозначные результаты, и Hibernate откажется получать ваши данные в одном запросе.
  3. Уже есть одна интересная ошибка, связанная с AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS . И будет еще, потому что, как говорят ребята из спящего режима: Примечание: это может происходить вне транзакции и небезопасно. Используйте с осторожностью. Вы в основном самостоятельно.

Лучший способ - сначала попробовать JOIN FETCH. Если это не сработает, попробуйте метод получения. Если это запуталось во время выполнения JIT-компилятором, присвойте результат public static volatile Object.

Или прекратите использовать Hibernate ...

14 голосов
/ 07 сентября 2012

Обратите внимание, что вы не должны использовать hibernate.enable_lazy_load_no_trans pre Hibernate 4.1.7, поскольку он пропускает соединения. Смотри https://hibernate.onjira.com/browse/HHH-7524

8 голосов
/ 23 марта 2009

LazyInitializationException означает, что вы вызываете коллекцию после закрытия сеанса гибернации или после отсоединения объекта от сеанса.

Вам необходимо либо повторно присоединить объект к сеансу гибернации, либо изменить место, где вы вызываете коллекцию, либо переместить границу, где сеанс закрывается, на более высокий уровень.

5 голосов
/ 27 сентября 2016

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

FetchType.EAGER загрузка плохо влияет на производительность. Также есть анти-паттерны, такие как:

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

Иногда вам даже не нужны сущности, и проекция DTO еще лучше. Вы должны получать объекты только тогда, когда вам нужно изменить их. Для транзакций только для чтения DTO прогнозы лучше .

5 голосов
/ 23 февраля 2009

OpenSessionInView - это один шаблон для решения этой проблемы. Некоторая информация здесь:

http://www.hibernate.org/43.html

Вы должны быть осторожны при реализации этого паттерна и понимать последствия. Каждый раз, когда вы просматриваете ленивую ассоциацию в представлении, она запускает другой SQL-запрос для загрузки данных. Если ваши варианты использования таковы, что количество и размер этих запросов SQL невелики, это может не иметь значения. Убедитесь, что вы как минимум изменили настройки ведения журнала, чтобы видеть, какие запросы Hibernate «магически» выполняет в фоновом режиме для загрузки данных.

Также рассмотрите тип приложения, которое вы пишете. Если вы не имеете дело с удаленным взаимодействием (без веб-служб, без веб-клиента на основе AJAX), то OSIV может работать очень хорошо. Однако, если удаленный сериализатор начнет обходить весь граф объектов, он, вероятно, вызовет смешное количество SQL-запросов и нанесет вред вашей БД и серверу приложений.

4 голосов
/ 02 ноября 2011

Если вы используете коллекцию и хотите инициализировать ее с отложенной загрузкой, используйте эту коллекцию перед закрытием сессии. Если после этого сеанс закрывается, если вы хотите его использовать, вы получаете lazyinitializeException, потому что lazy по умолчанию попытается.

1 голос
/ 27 октября 2014

В руководствах по Oracle Java указано, что «корпоративные компоненты поддерживают транзакции, механизмы, которые управляют одновременным доступом к общим объектам». Итак, для решения проблем Lazy Fetch я создаю Java Session Bean без состояния, а затем получаю все необходимые подклассы, прежде чем вернуться из метода. Это позволяет избежать исключения ленивой выборки. Oracle также называет это «базовым шаблоном J2EE Session Façade». Этот шаблон кажется лучше, чем некоторые другие упомянутые практики.

0 голосов
/ 17 апреля 2018

Я работаю над проектом, целью которого является решение общих проблем JPA при отображении сущностей в DTO с использованием ModelMapper. Этот вопрос уже решен на проекте. Ссылка на проект: JPA Model Mapper

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

Источник: JPA Model Mapper

Таким образом, в проекте мы имеем дело с LazyInitializationException, устанавливая значение null для всех не загруженных объектов. Приведенные ниже примеры показывают, как это работает.

Повторное сопоставление объекта с нулевым значением для всех не загруженных объектов:

TypedQuery<SystemEntity> query =
        em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);

SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemEntity.class);

Повторное сопоставление объекта со значением DTO, равным null, для всех не загруженных объектов:

TypedQuery<SystemEntity> query =
        em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);

SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemDTO.class);

Для получения дополнительной информации см. JPA Model Mapper

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...