LazyInitializationException, несмотря на OpenSessionInViewFilter

Кажется, я случайно получаю следующее 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>

person Abdullah Jibaly    schedule 07.06.2011    source источник
comment
Вы уверены, что всегда используете getCurrentSession()? Также покажите свою конфигурацию LocalSessionFactoryBean.   -  person axtavt    schedule 07.06.2011
comment
Да, мы всегда используем getCurrentSession(), я обновил определение компонента выше.   -  person Abdullah Jibaly    schedule 07.06.2011
comment
Другая возможность заключается в том, что вы аннулировали сеанс, к которому был присоединен объект, вызвав исключение из метода @Transactional.   -  person Affe    schedule 07.06.2011
comment
@AbdullahJibaly Как здесь hibernateFilter сопоставляется с сервлетом Dispatcher? У меня точно такая же проблема, хотя я дал все, что нужно.   -  person tintin    schedule 09.09.2012


Ответы (2)


Я знаю две наиболее распространенные причины исключений отложенной загрузки с включенным фильтром: либо попытка доступа к чему-то после того, как исключение сделало недействительным сеанс Hibernate, либо попытка доступа к полю чего-то, что на самом деле сидело без дела. в сеансе Веб и не прикреплен.

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";
  }
}
person Affe    schedule 07.06.2011
comment
Одна вещь, которую я заметил в своей трассировке стека, это отсутствие контроллера. Может ли это как-то быть связано с проблемой? - person Abdullah Jibaly; 07.06.2011
comment
Мы не используем сеансы, поэтому я уверен, что это не последнее, мне нужно изучить вызовы службы транзакций. Спасибо за совет. - person Abdullah Jibaly; 07.06.2011
comment
Да, из вашей трассировки стека ясно, что это происходит в JSP, но основная причина может быть той же: сеанса больше нет, потому что он был закрыт откатом. Возможно, вы сможете помочь больше, если вы опубликуете код. На самом деле не могу сделать много выводов только из конфигурации, кроме того, что выглядит нормально :) - person Affe; 08.06.2011
comment
Я смог убедиться, что причиной действительно было исключение, вызывающее откат. Поскольку это было в цикле повторных попыток, я не понимал, что это было причиной. Я делаю обновление модели и вручную вызываю исключение во время выполнения. Вторая повторная попытка прошла правильно, но я получил исключение выше в JSP. Имеет ли смысл то, что сессия все еще существует, пока не попадет в JSP? - person Abdullah Jibaly; 08.06.2011
comment
Если «исходный» сеанс становится недействительным, Spring прозрачно создаст другой, когда вы сделаете что-то новое, для чего он нужен. «Последняя» HSession будет доступна, когда вы перейдете к JSP, но это по-прежнему оставляет объекты, загруженные в первом сеансе, в отсоединенном состоянии. - person Affe; 08.06.2011
comment
Это должно быть так, теперь это имеет большой смысл. Ты жжешь! - person Abdullah Jibaly; 08.06.2011

Весной MVC вместо фильтра следует использовать OpenSessionInViewInterceptor. В моем текущем проекте это настроено следующим образом:

<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

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

person danny.lesnik    schedule 07.06.2011
comment
Я попробую, но после некоторых первоначальных исследований мне трудно найти какие-либо убедительные различия между ними. Знаете любую хорошую ссылку на это? - person Abdullah Jibaly; 07.06.2011
comment
На самом деле, я добавил его из руководства Spring, и у меня хорошо работал только перехватчик. Я думаю, вам нужно убедиться, что ваши транзакции управляются Spring с использованием @TRansactional или Aspects. - person danny.lesnik; 07.06.2011
comment
Похоже, мне нужно использовать версию фильтра, так как я использую SiteMesh (которому в моем случае нужен доступ к сеансу Hibernate). - person Abdullah Jibaly; 07.06.2011
comment
Да, в вашем случае это имеет смысл. Тогда, вероятно, вам нужно проверить свою транзакцию, может быть, вы закрываете транзакцию, а SiteMash все еще ее использует. - person danny.lesnik; 08.06.2011
comment
@Abdullah, где ты можешь решить свою проблему? Кажется, в настоящее время вы находитесь в своем положении, используя sitemesh и freemarker.using транзакцию на уровне обслуживания, но все еще имеете проблему Lazyinitialization. - person black sensei; 23.02.2012
comment
@blacksensei в нашем случае оказалось, что внутри транзакционного раздела выбрасывалось исключение (которое закрывает сеанс и воссоздает новый). В итоге мы всегда перечитывали объект из базы данных при каждой повторной попытке. - person Abdullah Jibaly; 23.02.2012
comment
@Abdullah спасибо за ответ, еще один вопрос. ваш перехватчик в контексте приложения или фильтр в web.xml - person black sensei; 24.02.2012
comment
@blacksensei мы не использовали подход перехватчика, то, как мы определили его как фильтр (определенный в web.xml), как показано в моем вопросе, работало нормально. - person Abdullah Jibaly; 24.02.2012
comment
Спасибо, это работает для меня. Раньше мне приходилось использовать @Proxy(lazy = false) в классах сущностей. Вам не нужно использовать LocalSessionFactoryBean. ‹bean id=sessionFactory class=org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean p:dataSource-ref=dataSource p:configLocation=${hibernate.config} p:packagesToScan=${hibernate.packagesToScan} /› работает отлично - person Pramod; 22.07.2012