it-roy-ru.com

Как решить LazyInitializationException при использовании JPA и Hibernate

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

@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, чтобы избежать этой ошибки?

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

48
rupertin

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-говорят»)

60
andreak

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

  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 ...

16
jb.

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

14
Hani

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

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

7
Daniel Alexiuc

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

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

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

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

5
cliff.meyers

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

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

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

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

5
Vlad Mihalcea

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

4
SANTOSH Kumar

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

1
K.Nicholas

Я работаю над проектом, целью которого является решение общих проблем 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 с установкой нулевого значения для всех не загруженных объектов:

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

0
Vinícius M. Freitas