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(объект);
Ответ 10
Если вы управляете сеансом Hibernate вручную, вы можете посмотреть в sessionFactory.getCurrentSession() и связанных с ним документах:
http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html
Ответ 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), сразу после загрузки ваш корневой объект.