Hibernate: LazyInitializationException: не удалось инициализировать прокси

Вот один, который меня озадачил. Я пытаюсь реализовать базовую структуру DAO Hibernate, но у меня проблема.

Вот основной код:

int startingCount = sfdao.count();
sfdao.create( sf );
SecurityFiling sf2 = sfdao.read( sf.getId() );
sfdao.delete( sf );
int endingCount = sfdao.count();

assertTrue( startingCount == endingCount );
assertTrue( sf.getId().longValue() == sf2.getId().longValue() );
assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) );
assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) );

Он терпит неудачу на третьем assertTrue, где он пытается сравнить значение в sf с соответствующим значением в sf2. Вот исключение:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at com.freightgate.domain.SecurityFiling_$$_javassist_7.getSfSubmissionType(SecurityFiling_$$_javassist_7.java)
    at com.freightgate.dao.SecurityFilingTest.test(SecurityFilingTest.java:73)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40)
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
72
0
205 397
14

Ответы 14

Обычно это означает, что сеанс Hibernate-владельца уже закрыт. Вы можете исправить это одним из следующих способов:

  1. какой бы объект ни создавал эту проблему, используйте HibernateTemplate.initialize(object name)
  2. Используйте lazy=false в своих файлах hbm.

Была та же проблема, и lazy = false исправил ее. Спасибо

autonomatt 30.12.2009 21:54

теперь в моем случае я использую lazy=false для всех уровней dao, но оказалось, что из-за этого производительность приложения низкая, попытался установить lazy=true, но теперь генерируются lazyException, любые предложения, как это можно исправить.

Rachel 08.02.2012 20:12

пакоре, не могли бы вы указать, почему нет решения и как его понять?

Victor 07.11.2013 00:06

@Victor lazy = false - это то же самое, что и нетерпеливый. Когда мы выбираем использование активной ассоциации загрузки, каждый раз, когда мы загружаем объект, все «активные ассоциации» будут загружаться, даже если мы ее не запрашиваем и не используем.

Jonathas Pacífico 26.11.2013 22:31

Если вы управляете сеансом Hibernate вручную, вы можете посмотреть здесь sessionFactory.getCurrentSession () и связанные с ним документы:

http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html

Ладно, наконец-то понял, в чем я был упущен. Я ошибочно полагал, что должен заключать каждый метод DAO в транзакцию. Ужасно неправильно! Я усвоил урок. Я взял весь код транзакции из всех методов DAO и настроил транзакции строго на уровне приложения / менеджера. Это полностью решило все мои проблемы. Данные правильно загружаются лениво по мере необходимости, упаковываются и закрываются, как только я выполняю фиксацию.

Жить хорошо ... :)

Я не уверен, что полностью понимаю, так как не припомню, чтобы видел это в других проектах. Но вы правы: добавление @org.springframework.transaction.annotation.Transactional(re‌​adOnly=true) к методам на уровне обслуживания устранило проблему. (На этом уровне мы получаем объект и передаем его другому вызову в DAO.)

Arjan 03.12.2012 14:49

Я думаю, что в своем ответе Пико имел в виду, что существует файл hbm. У меня есть файл Tax.java. Информация о сопоставлении сохраняется в файле hbm (= отображение гибернации). В теге класса есть свойство под названием ленивый. Установите для этого свойства значение true. В следующем примере hbm показан способ установки свойства lazy на ложный.

` я бы ... '

Если вы используете аннотации, посмотрите документацию по спящему режиму. http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/

Надеюсь, это помогло.

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

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) для сеанса EJB без сохранения состояния

Мы тоже столкнулись с этой ошибкой. Чтобы решить эту проблему, мы добавили ленивый = ложный в файл сопоставления Hibernate.

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

Чтобы получить доступ к этому классу B, мы должны были указать в файле сопоставления Hibernate класса A атрибут lazy = false. Например,

     <many-to-one name = "classA" 
                 class = "classB"
                 lazy = "false">
        <column name = "classb_id"
                sql-type = "bigint(10)" 
                not-null = "true"/>
    </many-to-one>  

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

session.update(object);

Использование lazy=false не является хорошим решением, потому что вы отказываетесь от функции ленивой инициализации в спящем режиме. При lazy=false коллекция загружается в память одновременно с запросом объекта. Это означает, что если у нас есть коллекция из 1000 элементов, все они будут загружены в память, независимо от того, собираемся мы к ним обращаться или нет. А это нехорошо.

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

Если вы используете спящий режим с аннотациями JPA, это будет полезно. В вашем классе обслуживания должен быть установщик для диспетчера сущностей с @PersistenceContext. измените это на @PersistenceContext (type = PersistenceContextType.EXTENDED). Тогда вы можете получить доступ к ленивому свойству в любом месте.

Это неверно, если вы не управляете своими транзакциями вручную. Тип контекста постоянства Spring EXTENDED предназначен для шаблона длительного разговора, а не для шаблона сеанса на запрос, о котором спрашивает OP.

HDave 18.10.2011 23:48

Если вы знаете о влиянии lazy=false и по-прежнему хотите использовать его по умолчанию (например, для целей прототипирования), вы можете использовать любое из следующих действий:

  • если вы используете конфигурацию XML: добавьте default-lazy = "false" в свой элемент <hibernate-mapping>
  • если вы используете конфигурацию аннотации: добавьте @Proxy(lazy=false) в свой класс (ы) сущности

См. Мою статью. У меня была такая же проблема - LazyInitializationException - и вот ответ, который я наконец придумал:
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
Установка lazy = false не является ответом - она ​​может загружать все сразу, и это не обязательно хорошо. Пример:
1 таблица записей A ссылки:
5 записей таблица B ссылки:
25 записей, таблица C, ссылки:
125 записей таблица D
...
и т. д. Это лишь один пример того, что может пойти не так. - Тим Сабин

Вы должны объяснить решение здесь, а не ссылаться на сторонний веб-сайт.

saimiris_devel 16.12.2016 17:57

используйте Hibernate.initialize для ленивого поля

Кажется, только ваш DAO использует сеанс. Таким образом, новый сеанс открывается, а затем закрывается для каждого вызова метода DAO. Таким образом, выполнение программы может быть возобновлено как:

// open a session, get the number of entity and close the session
int startingCount = sfdao.count();

// open a session, create a new entity and close the session
sfdao.create( sf );

// open a session, read an entity and close the session
SecurityFiling sf2 = sfdao.read( sf.getId() );

// open a session, delete an entity and close the session
sfdao.delete( sf );

etc...

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

sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() )

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

Есть два подхода к решению этой проблемы:

  • создать сеанс, в который будет заключен весь наш код. Таким образом, это будет означать изменение вашего содержимого DAO, чтобы избежать открытия второго сеанса.

  • создайте сеанс, а затем обновите (т.е. переподключите) вашу сущность к этому сеансу перед утверждениями.

    session.update (объект);

По умолчанию все ассоциации one-to-many и many-to-many выбираются лениво при первом доступе.

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

transactionTemplate.execute(new TransactionCallback<Void>() {
    @Override
    public Void doInTransaction(TransactionStatus transactionStatus) {

        int startingCount = sfdao.count();

        sfdao.create( sf );

        SecurityFiling sf2 = sfdao.read( sf.getId() );

        sfdao.delete( sf );

        int endingCount = sfdao.count();

        assertTrue( startingCount == endingCount );
        assertTrue( sf.getId().longValue() == sf2.getId().longValue() );
        assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) );
        assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) );

        return null;
    }
});

Другой вариант - получить все LAZY-ассоциации при загрузке вашей сущности, чтобы:

SecurityFiling sf2 = sfdao.read( sf.getId() );

должен также получить LAZY submissionType:

select sf
from SecurityFiling sf
left join fetch.sf.submissionType

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

Вы можете получить любое количество ассоциаций [one|many]-to-one и одну ассоциацию списка «[один | много] ко многим» (из-за выполнения декартового произведения).

Чтобы инициализировать несколько «[один | много]-ко-многим», вы должны использовать Hibernate.initialize (коллекция) сразу после загрузки вашего корневого объекта.

Если вы используете аннотации Spring и JPA, самый простой способ избежать проблем с сеансом при ленивой инициализации - это воспроизвести:

@PersistenceContext   

к

@PersistenceContext(type = PersistenceContextType.EXTENDED)

Это работает, только если вы управляете своими транзакциями вручную.

Gemasoft 08.12.2015 06:55

Другие вопросы по теме