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





Обычно это означает, что сеанс Hibernate-владельца уже закрыт. Вы можете исправить это одним из следующих способов:
HibernateTemplate.initialize(object name)lazy=false в своих файлах hbm.теперь в моем случае я использую lazy=false для всех уровней dao, но оказалось, что из-за этого производительность приложения низкая, попытался установить lazy=true, но теперь генерируются lazyException, любые предложения, как это можно исправить.
пакоре, не могли бы вы указать, почему нет решения и как его понять?
@Victor lazy = false - это то же самое, что и нетерпеливый. Когда мы выбираем использование активной ассоциации загрузки, каждый раз, когда мы загружаем объект, все «активные ассоциации» будут загружаться, даже если мы ее не запрашиваем и не используем.
Если вы управляете сеансом Hibernate вручную, вы можете посмотреть здесь sessionFactory.getCurrentSession () и связанные с ним документы:
http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html
Ладно, наконец-то понял, в чем я был упущен. Я ошибочно полагал, что должен заключать каждый метод DAO в транзакцию. Ужасно неправильно! Я усвоил урок. Я взял весь код транзакции из всех методов DAO и настроил транзакции строго на уровне приложения / менеджера. Это полностью решило все мои проблемы. Данные правильно загружаются лениво по мере необходимости, упаковываются и закрываются, как только я выполняю фиксацию.
Жить хорошо ... :)
Я не уверен, что полностью понимаю, так как не припомню, чтобы видел это в других проектах. Но вы правы: добавление @org.springframework.transaction.annotation.Transactional(readOnly=true) к методам на уровне обслуживания устранило проблему. (На этом уровне мы получаем объект и передаем его другому вызову в DAO.)
Я думаю, что в своем ответе Пико имел в виду, что существует файл 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 прав; см. также В чем разница между контекстом сохраняемости в области транзакции и контекстом расширенного сохранения?
Если вы знаете о влиянии lazy=false и по-прежнему хотите использовать его по умолчанию (например, для целей прототипирования), вы можете использовать любое из следующих действий:
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
...
и т. д. Это лишь один пример того, что может пойти не так.
- Тим Сабин
Вы должны объяснить решение здесь, а не ссылаться на сторонний веб-сайт.
используйте 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)
Это работает, только если вы управляете своими транзакциями вручную.
Была та же проблема, и lazy = false исправил ее. Спасибо