2008-12-06 5 views
67

Вот один из них, который меня озадачил. Я пытаюсь реализовать базовую структуру DAB Hibernate, но у меня проблема.hibernate: LazyInitializationException: невозможно инициализировать прокси

Вот существенный код:

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, где он пытается сравнить значение в НФ к соответствующему значению в 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) 

ответ

15

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

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

был такой же проблема и ленивые = ложь зафиксировала его , Спасибо – autonomatt 2009-12-30 18:54:29

+1

теперь в моем случае я использую `lazy = false` для всех уровней dao, но оказывается, что из-за этого производительность приложения медленная, пыталась установить` lazy = true`, но теперь lazyException выбрасывается, любые предложения, как это может быть исправлено. – Rachel 2012-02-08 16:12:18

+0

pakore, не могли бы вы указать, почему это не решение и как его понять? – Victor 2013-11-06 20:06:42

2

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

Жизнь блистательное ... :)

+0

Я не уверен, что полностью понимаю, поскольку я не помню, чтобы видеть это в других проектах. Но вы правы: добавление `@ org.springframework.transaction.annotation.Transactional (readOnly = true)` к методам уровня обслуживания устраняет проблему. (В этом слое мы извлекаем объект и передаем его другому вызову DAO.) – Arjan 2012-12-03 10:49:01

1

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

` ID ...

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

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

4

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

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) для сессионного EJB

3

Мы столкнулись с этой ошибкой, а также. Что мы сделали для решения проблемы, мы добавили lazy = false в файл сопоставления 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> 
68

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

session.update(object); 

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

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

7

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

2

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

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

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

2

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

0

По умолчанию все 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()); 

должен принести ленивый submissionType тоже:

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

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

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

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

1

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

@PersistenceContext 

в

@PersistenceContext(type = PersistenceContextType.EXTENDED) 
Смежные вопросы