Как решить исключение 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, чтобы избежать этой ошибки?

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

Ответ 1

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

Вам нужно установить свойство hibernate-hibernate.enable_lazy_load_no_trans = true

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

<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 при навигации по вашей модели домена за пределами сеанса спящего режима (persistence-context в "JPA-talk" )

Ответ 2

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

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

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

  • Вызов получателей может быть оптимизирован компилятором JIT (иногда это занимает некоторое время).
  • Объекты, которые вы пытаетесь использовать JOIN FETCH, могут быть связаны через несколько "много" связей, связанных с List. В этом случае результирующий запрос возвращает неоднозначные результаты, а Hibernate отказывается извлекать данные в одном запросе.
  • Существует уже некоторая интересная ошибка, связанная с доступными настройками .ENABLE_LAZY_LOAD_NO_TRANS. И будет больше, потому что, как говорят спящие парни: Примечание: это может произойти за пределами транзакции и небезопасно. Используйте с осторожностью. Вы сами по себе.

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

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

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

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

Ответ 6

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

Ответ 7

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

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

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

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

Ответ 8

Учебники Oracle Java указывают, что "Enterprise beans поддерживают транзакции, механизмы, управляющие параллельным доступом к общим объектам". Поэтому, чтобы обрабатывать проблемы с Lazy Fetch, я создаю сеанс Java без состояния без ограничений Bean, а затем получаю все вспомогательные классы, которые мне нужны, прежде чем возвращаться из метода. Это позволяет избежать исключения ленивой выборки. Oracle также упоминает это как основной шаблон J2EE "Фаза фасада сеанса". Этот образец кажется лучше, чем некоторые другие упомянутые практики.

Ответ 9

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

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

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

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

Перенос сущности, задающей значение 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