Org.hibernate.LazyInitializationException: не удалось инициализировать прокси-сервер - нет сеанса, на бис 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 имеет это: при доступе к lazyloaded коллекции, когда сеанс открыт, с помощью метода tranactal:
@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();
Ответы
Ответ 1
По умолчанию FetchType
в ManyToMany
есть LAZY
, а документация hibernate для работает с ленивыми ассоциациями явно вызывает такой вид доступа как ошибку. Вы можете взаимодействовать с лениво связанными объектами только в то время, когда сеанс все еще открыт. Эта часть документации также предоставляет альтернативы для доступа к таким лениво связанным элементам объекта. Мы предпочитаем указывать режим выборки как JOIN
в используемых критериях, в наших приложениях
Edit
Set<Foo> favourites = user.getFavourites();
Вышеприведенный оператор фактически не возвращает набор, содержащий все объекты Foo
. Это просто прокси. Фактические объекты Foo
извлекаются только тогда, когда доступны элементы в наборе, такие как favorites.iterator()
и т.д. Эта операция явно происходит вне вашего метода getFavorites()
. Но аннотация @Transactional
по методу getFavorites()
указывает, что сеанс будет закрыт в конце этого метода.
Итак, когда методы вызываются в избранном наборе, сеанс уже закрыт и, следовательно, исключение.
Чтобы устранить это, вы должны использовать объект Criteria для извлечения пользователя и указать тип выборки как JOIN
, чтобы объекты Foo были заполнены в возвращаемом объекте User.
Ответ 2
Существует два решения.
-
Не используйте ленивую загрузку.
Установить lazy=false
в XML или Установить @OneToMany(fetch = FetchType.EAGER)
В аннотации.
-
Используйте ленивую загрузку.
Установить lazy=true
в XML или Установить @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>
- это имя sessionFacory bean, которое определено в applicationContext.xml
Ответ 3
Да, к объекту следует обращаться в транзакционном контексте, в противном случае операция выдаст LazyInitializationException
.