Исключение LazyInitializationException, несмотря на OpenSessionInViewFilter

#java #hibernate #spring #open-session-in-view

#java #переход в спящий режим #spring #открыть сеанс в режиме просмотра

Вопрос:

Кажется, я случайным образом получаю следующее исключение LazyInitializationException в приложении Spring / MVC 3.0 / Hibernate 3.5, несмотря на то, что вижу фильтр в самой трассировке стека. Есть идеи, на что мне следует обратить внимание?

 07 Jun 2011 13:48:47,152 [ERROR]  (http-3443-2) org.hibernate.LazyInitializationException: could not initialize proxy - no Session
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.test.Image_$$_javassist_18.getMyKey(Image_$$_javassist_18.java)
    at com.test.AppTagHelper.getAssetUrl(AppTagHelper.java:66)
    at sun.reflect.GeneratedMethodAccessor81.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.el.parser.AstFunction.getValue(AstFunction.java:110)
    at org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:186)
    at org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:935)
    at org.apache.jsp.WEB_002dINF.view.home_jsp._jspx_meth_c_005fout_005f2(home_jsp.java:1027)
    at org.apache.jsp.WEB_002dINF.view.home_jsp._jspx_meth_c_005fwhen_005f1(home_jsp.java:1002)
    at org.apache.jsp.WEB_002dINF.view.home_jsp._jspx_meth_c_005fchoose_005f1(home_jsp.java:969)
    at org.apache.jsp.WEB_002dINF.view.home_jsp._jspx_meth_display_005fcolumn_005f0(home_jsp.java:867)
    at org.apache.jsp.WEB_002dINF.view.home_jsp._jspService(home_jsp.java:214)
    <<VARIOUS SPRING FILTERS>>
    at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
  

Из web.xml :

 <filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>
        org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    <init-param>
    <param-name>sessionFactoryBeanName</param-name>
        <param-value>sessionFactory</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>hibernateFilter</filter-name>
    <url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>hibernateFilter</filter-name>
    <url-pattern>*.json</url-pattern>
</filter-mapping>
  

Обновление, добавляющее определение SessionFactoryBean :

 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" depends-on="dataSource">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="com.test.model" />
    <property name="schemaUpdate" value="false" />
    <property name="eventListeners">
      <map>
        <!-- Create -->
        <entry key="pre-insert">
          <ref local="hibernateCreateListener"/>
        </entry>
        <entry key="post-insert">
          <ref local="hibernateRevisionListener"/>
        </entry> 
        <!-- Update -->
        <entry key="pre-update">
          <ref local="hibernateUpdateListener"/>
        </entry>
        <entry key="post-update">
          <ref local="hibernateRevisionListener"/>
        </entry>
        <entry key="post-delete">
          <ref local="hibernateRevisionListener"/>
        </entry>
        <entry key="pre-collection-update">
          <ref local="hibernateRevisionListener"/>
        </entry>
        <entry key="post-collection-recreate">
          <ref local="hibernateRevisionListener"/>
        </entry>
        <entry key="pre-collection-remove">
          <ref local="hibernateRevisionListener"/>
        </entry>                   
      </map>
    </property>
    <property name="entityInterceptor">
      <ref bean="hibernateAuditInterceptor"/>
    </property>
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
        <prop key="hibernate.default_schema">${app.databaseSchema}</prop>                 
        <prop key="hibernate.show_sql">false</prop>
        <prop key="hibernate.format_sql">false</prop>
        <prop key="hibernate.use_sql_comments">false</prop>
        <prop key="hibernate.connection.oracle.jdbc.ReadTimeout">60000</prop>
        <prop key="org.hibernate.envers.revisionTypeFieldName">REV_TYPE</prop>
        <prop key="org.hibernate.envers.revisionFieldName">REV_ID</prop>        
      </props>
    </property>
  </bean>
  

Комментарии:

1. Вы уверены, что всегда используете getCurrentSession() ? Кроме того, покажите свою конфигурацию LocalSessionFactoryBean .

2. Да, мы всегда используем getCurrentSession() , я обновил определение компонента выше.

3. Другая возможность заключается в том, что вы сделали недействительным сеанс, к которому был присоединен объект, вызвав исключение из метода @Transactional.

4. @AbdullahJibaly Как здесь hibernateFilter сопоставляется с сервлетом диспетчера? Я получаю точно такую же проблему, хотя я предоставил все, что необходимо.

Ответ №1:

Две наиболее распространенные причины, о которых я знаю для исключений отложенной загрузки с включенным фильтром, — это либо попытка получить доступ к чему-либо после того, как исключение сделало недействительным сеанс гибернации, либо попытка получить доступ к полю чего-либо, что на самом деле находилось в веб сеансе и не подключено.

 public interface EntityService {

  @Transactional
  EntityA getA(Long id);

  @Transactional
  EntityB getB(Long id);
}

public class WebPageController {

  public void handleGet(Long id1, Long id2) {
    EntityA a = entityService.getA(id1);
    try {
      EntityB b = entityService.getB(id2);
    } catch (Exception e) {
      //print somthething
    }
    a.accessLazyField(); //will throw lazy load after getB throws exception
  }
}
  

Очевидно, что много кода и аннотаций опущено для ясности 🙂

 @SessionAttributes("model")
public class WebPageController {

  @ModelAttribute("model")
  @RequestMapping(method=RequestMethod.GET)
  public EntityA handleGet(Long id) {
    return entityService.getA(id);
  }

  @RequestMapping(method=RequestMethod.POST)
  public String handlePost(@ModelAttribute("model") EntityA a) {
    a.accessLazyField(); //will throw lazy load if the field was not accessed during original page rendering
    return "viewName";
  }
}
  

Комментарии:

1. Одна вещь, которую я заметил в моем stacktrace, заключается в том, что нет контроллера. Может ли это как-то быть связано с проблемой?

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

3. Да, из вашей трассировки стека ясно, что это происходит в JSP, но основная причина все еще может быть той же, сессия больше не существует, потому что она была закрыта откатом. Возможно, вы сможете помочь больше, если опубликуете некоторый код. На самом деле не могу сделать никаких выводов только из конфигурации, кроме «выглядит нормально» 🙂

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

5. Если «исходный» сеанс становится недействительным, Spring прозрачно создаст другой, когда вы сделаете что-то новое, что в нем нуждается. «Последняя» HSession будет доступна, когда вы перейдете к JSP, но это все равно оставляет объекты, загруженные в первом сеансе, в отключенном состоянии.

Ответ №2:

В spring MVC вы должны использовать OpenSessionInViewInterceptor вместо filter В моем текущем проекте это настроено таким образом:

 <mvc:annotation-driven/>
<mvc:interceptors>
<bean class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
</mvc:interceptors>
  

Где SessionFactory ссылается на org.springframework.orm.hibernate3.LocalSessionFactoryBean

Работает безупречно.

Комментарии:

1. Я попробую это сделать, но после некоторых первоначальных исследований мне трудно найти какие-либо окончательные различия между ними. Знаете какую-нибудь хорошую ссылку на это?

2. На самом деле, я добавил это из руководства Spring, и у меня хорошо работал только interceptor. Я думаю, вам нужно убедиться, что ваши транзакции управляются Spring с использованием @TRansactional или Aspects.

3. Похоже, мне придется использовать версию фильтра, поскольку я использую SiteMesh (в моем случае которой требуется доступ к сеансу гибернации).

4. Да, в вашем случае это имеет смысл. Тогда, вероятно, вам нужно проверить вашу транзакцию, возможно, вы закрываете транзакцию, в то время как SiteMash все еще использует ее.

5. @Abdullah где вы можете решить свою проблему? похоже, в настоящее время вы находитесь в вашем положении, используя sitemesh и freemarker.используя транзакцию на уровне сервиса, но все еще испытывая проблему с ленивой инициализацией.