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

Вот тот, который меня озадачил. Я пытаюсь реализовать базовую структуру DAB 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)

Ответ 1

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

session.update(object);

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

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) для сеанса бездействия EJB

Ответ 6

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

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

Чтобы получить доступ к этому классу B, нам нужно было указать в файле сопоставления типа Hibernate атрибут 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>  

Ответ 7

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

Жизнь хороша...:)

Ответ 8

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

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

Ответ 9

Кажется, что только ваш 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(объект);

Ответ 11

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

`                      Я бы      ...

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

Я надеюсь, что это помогло.

Ответ 12

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

Ответ 13

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

@PersistenceContext   

к

@PersistenceContext(type = PersistenceContextType.EXTENDED)

Ответ 14

По умолчанию все ассоциации 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 и одну ассоциацию "[one | many] -to-many" (из-за работы с декартовым продуктом).

Чтобы инициализировать несколько "[one | many] -to-many", вы должны использовать Hibernate.initialize(collection), сразу после загрузки ваш корневой объект.