2009-02-23 2 views
47

Я работаю над проектом для клиента, который хочет использовать ленивую инициализацию. Они всегда получают «ленивое исключение инициализации» при сопоставлении классов с режимом ленивой загрузки по умолчанию.Как решить 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, чтобы избежать этой ошибки?

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

ответ

5

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

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

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

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

+0

Не используйте открытый сеанс, если вы ожидаете загрузки на свой сервер. Этот ответ мог бы использовать предупреждение в этом направлении. – iwein

+1

Я думал, что ссылки будет достаточно, чтобы объяснить последствия шаблона, но я добавил некоторые предупреждения на всякий случай. :) –

7

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

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

+12

что приятно, но как? –

+2

Точно, мы получили ЧАСТЬ ЧТО; КАК это вопрос! – Nikhil

16

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

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

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

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

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

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

+0

Для точки (3) стоит отметить, что проблема связана с ошибкой, закрытой с версии 4.1.7. На момент написания этой статьи есть еще одна проблема: https: //hibernate.atlassian.net/browse/HHH-8782. –

+2

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

4

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

60

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

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

+3

Было ли мне приятно это видеть! Огромное спасибо. – Oversteer

+1

Не могли бы вы добавить к своему ответу то, что именно это свойство делает? Реализация может иметь серьезные последствия для согласованности данных или производительности. Возможно, и то, и другое. – iwein

+1

Эта опция загружает ленивую ассоциацию вне транзакции, а это значит, что данные могут не соответствовать тому, что принадлежит родительскому объекту. Но это не хуже, чем использование SpringEntityManagerInViewFilter от Spring, которое также загружает вещи за пределами транзакции. Я использую картографирование на основе полей и не испытывал JIT, запутывая сгенерированные прокси. – andreak

14

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

+1

это работает, но теперь у меня есть проблема с рекурсивным циклом с отображением гибернации –

1

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

2

The best way to solve the LazyInitializationException должен использовать директиву JOIN FETCH в ваших запросах сущности.

EAGER loading плохой для производительности. Кроме того, есть антишаблоны, такие как:

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

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

Смежные вопросы