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.

Есть два решения.

  1. Не используйте ленивый груз.

    Задавать lazy=false в XML или Set @OneToMany(fetch = FetchType.EAGER) В аннотации.

  2. Используйте ленивый груз.

    Задавать 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.

Другие вопросы по тегам