org.hibernate.LazyInitializationException: не удалось инициализировать прокси-сервер - нет Session, encore un fois
Foo выглядит так:
@ManyToMany
private Set<User> favouritedBy;
пока пользователь имеет это:
@ManyToMany(mappedBy = "favouritedBy")
private Set<Foo> favourites = new HashSet<Foo>();
public Set<Foo> getFavourites() {
return favourite;
}
И у fooService это так: доступ к отложенной коллекции осуществляется при открытии сеанса с помощью транзакционного метода:
@Transactional(readOnly = true)
public Set<Foo> getFavourites(User user) {
user = dao.get(User.class, user.getId()); //the dao gets a session
Set<Foo> favourites = user.getFavourites();//but the session is not here and the exception is thrown?
return favourties;
}
РЕДАКТИРОВАТЬ Это исправляет это, не используя критерии:
Set<Foo> favourites = new HashSet<Foo>(user.getFavourites());
и это исправляет критерии
Session session = sessionFactory.getCurrentSession();
final Criteria crit = session.createCriteria(Foo.class);
crit.setFetchMode("favourites", FetchMode.JOIN);
crit.add(Property.forName("id").eq(id));
return (Foo) crit.uniqueResult();
3 ответа
По умолчанию FetchType
в ManyToMany
является LAZY
и документация hibernate для работы с ленивыми ассоциациями явно называет этот вид доступа ошибкой. Вы можете взаимодействовать с лениво связанными объектами, только когда сессия еще открыта. Эта часть документации также предоставляет альтернативы для доступа к таким лениво связанным элементам объекта. Мы предпочитаем указывать режим извлечения как JOIN
в критериях, используемых в наших приложениях
Редактировать:
Set<Foo> favourites = user.getFavourites();
Вышеупомянутое утверждение фактически не возвращает набор, который содержит все Foo
объекты. Это просто прокси. Настоящий Foo
объекты выбираются только тогда, когда к элементам в наборе обращаются как favorites.iterator()
и т. д., эта операция явно происходит за пределами вашего getFavorites()
метод. Но @Transactional
аннотация на getFavorites()
Метод указывает, что сессия будет закрыта в конце этого метода.
Таким образом, когда методы вызываются для набора избранного, сессия уже закрыта и, следовательно, исключение.
Чтобы решить эту проблему, вы должны использовать объект Criteria для извлечения пользователя и указать тип выборки как JOIN
так что объекты Foo заполняются в возвращаемом объекте User.
Есть два решения.
Не используйте ленивый груз.
Задавать
lazy=false
в XML или Set@OneToMany(fetch = FetchType.EAGER)
В аннотации.Используйте ленивый груз.
Задавать
lazy=true
в XML или Set@OneToMany(fetch = FetchType.LAZY)
В аннотации.и добавьте фильтр в свой
web.xml
<listener> ... </listener> <filter> <filter-name>hibernateFilter</filter-name> <filter-class> org.springframework.orm.hibernate4.support.OpenSessionInViewFilter </filter-class> <init-param> <param-name>sessionFactoryBeanName</param-name> <param-value>mySessionFactory</param-value> </init-param> </filter> <filter-mapping> <filter-name>hibernateFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> ... </servlet>
А также <param-value>mySessionFactory</param-value>
ваше имя сессионного компонента, определенное в applicationContext.xml
Да, к объекту следует обращаться в транснациональном контексте, иначе он выдаст LazyInitializationException
,
Если вы используете какие-либо отношения @...Many... вместе с типом Fetch "Lazy" и получаете исключение LazyInitializationException - это означает, что у вас отключен OpenInView, это хорошо.
Чтобы избежать как LazyInitializationException, так и включения OIV (что делает сеанс Hibernate открытым дольше, чем в большинстве случаев необходимо) -убедитесь, что вы указали @Fetch(FetchMode.JOIN) в столбце выдачи.
Пример: До:
@ManyToMany(fetch = FetchType.LAZY)
private Set<Kek> keks;
После:
@Fetch(FetchMode.JOIN)
@ManyToMany(fetch = FetchType.LAZY)
private Set<Kek> keks;
Таким образом, вы принудительно активируете тип Join Fetch, который (очень просто) будет предоставлять правильный запрос с необходимыми связанными объектами Joined, а не заставлять вас использовать Eager fetch.