Hibernate延迟初始化异常

时间:2009-11-03 07:39:30

标签: java hibernate

我正在尝试使用spring mvc和hibenate创建一个应用程序。我已经看到异常failed to lazily initialize a collection of role近两天了:(如果我急切加载集合,应用程序运行正常。但我不希望这样。

我尝试在OpenSessionInViewFilter中实施web.xml,但错误仍然存​​在。我试图扩展OpenSessionInViewFilter并使用我自己的过滤器,即使现在问题仍未解决。这是我实施的过滤器

public class HibernateFilter extends OpenSessionInViewFilter {

    @Override
    protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {

  Session session = super.getSession(sessionFactory);
  session.setFlushMode(FlushMode.AUTO);
  return session;
    }

    @Override
    protected void closeSession(Session session, SessionFactory sessionFactory) {
        try {
            if (session != null && session.isOpen() && session.isConnected()) {
                try {
                    session.flush();
                } catch (HibernateException e) {
                    throw new CleanupFailureDataAccessException("Failed to flush session before close: " + e.getMessage(), e);
                } catch (Exception e) {
                }
            }
        } finally {
            super.closeSession(session, sessionFactory);
        }
    }
}

我在调试模式下运行了应用程序。我发现会话不是null,只有在通过控制器代码后才会调用closeSession。但是,如果我在会话打开时尝试在控制器中获取一个集合,它仍会失败:(这是我的web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/blog-servlet.xml</param-value>
</context-param>

<filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>
        core.HibernateFilter
    </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>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>blog</servlet-name>
    <servlet-class>
           org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>blog</servlet-name>
    <url-pattern>*.htm</url-pattern>
</servlet-mapping>

出了什么问题?


作为@sfussenegger mentioned ...我正在使用会话来识别当前用户。这是我的控制器代码:

@RequestMapping("/show.htm")  

public String show(ModelMap model, HttpSession session) { 

User u = (User) session.getAttribute("currentUser"); 

model.addAttribute("user", u);

if (u.getBlogs() != null) {

List<Blog> blogs = new ArrayList<Blog>(u.getBlogs());

model.addAttribute("myBlogs", blogs);

} 

return "show";
}

并且我的jsp迭代我在模型中收集的MYblogs


我的控制器代码:

  Integer uid = (Integer) session.getAttribute("currentUser");
    User user = getUserDao().findById(uid);
    model.addAttribute("user", user);
    if (user.getBlogs() != null) {
        List<Blog> blogs = new ArrayList<Blog>(user.getBlogs()); //fails here
        model.addAttribute("myBlogs", blogs);
    }
    return "show";
}
提出异常:

StandardWrapperValve[try-blog]: PWC1406: Servlet.service() for servlet try-blog threw exception
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ValueObjects.User.blogs, no session or session was closed
        at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
        at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
        at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:365)
        at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
        at org.hibernate.collection.PersistentSet.toArray(PersistentSet.java:194)
        at java.util.ArrayList.<init>(ArrayList.java:131)
        at Controllers.UserController.show(UserController.java:52)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.doInvokeMethod(HandlerMethodInvoker.java:421)
        at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:136)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:326)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:313)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:875)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:807)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:501)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:734)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
        at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:427)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:333)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
        at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:246)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:313)
        at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:287)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:218)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:94)
        at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:98)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:222)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)
        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1096)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:166)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)
        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1096)
        at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:288)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:647)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.doProcess(DefaultProcessorTask.java:579)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:831)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:341)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:263)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:214)
        at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:265)
        at com.sun.enterprise.web.connector.grizzly.ssl.SSLWorkerThread.run(SSLWorkerThread.java:106)

6 个答案:

答案 0 :(得分:3)

Dick是正确的,控制器类可能会做一些有趣的事情。

另一点是确保你的类路径中有所有必需的jar,例如cglib或javaassist。

使用spring openSessionInViewInterceptor而不是OpenSessionInViewFilter可能更容易(因为你已经在使用spring)。

只需在blog-servlet.xml配置中添加以下内容(当然可以将其拆分为多个文件)

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="interceptors"><list>    
        <ref bean="openSessionInViewInterceptor" />             
    </list></property>
</bean>
<bean id="openSessionInViewInterceptor" class="org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor">
    <property name="entityManagerFactory"><ref local="entityManagerFactory"/></property>    
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="ha-admin" />
    <property name="dataSource"  ref="dataSource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"  
            p:database="ORACLE" p:showSql="true" />
    </property>

    <property name="jpaPropertyMap">
    <props>                 


         <!-- Enable Hibernate statistics generation 
         <prop key="hibernate.cache.use_query_cache">true</prop>
         <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
         <prop key="hibernate.cache.use_second_level_cache">true</prop>
         <prop key="hibernate.cache.provider_configuration_file_resource_path">/ehcache.xml</prop>
        <prop key="hibernate.generate_statistics">true</prop>
        -->                                             
    </props>        
    </property>
</bean>

您需要在配置文件中添加数据源并从web.xml中删除hibernateFilter。

答案 1 :(得分:1)

您很可能正在使用分离对象,即已在不同会话中加载的对象,而不是OpenSessionInViewFilter创建的对象。当您在会话中存储对象并从后续请求访问它们时,可能会发生这种情况。 这可能吗?

编辑:

我不鼓励在会话中保留User对象。相反,只需保留id并在每次需要时从DB中获取User对象。因此,您应该使用类似

的方法,而不是当前的方法
User getUserFromSession(HttpSession session) {
  Integer userId = (Integer) session.getAttribute("currentUser"); 
  return userId != null ? getObjectById(User.class, userId) : null;
}

请注意,通过id获取对象非常快,特别是如果您已将Hibernate的二级缓存配置为存储User对象 - 那么请忘记任何性能注意事项。但主要的优点是您不必再处理分离的对象了。分离的物体是邪恶的,没有人喜欢它们! ;)

正如@skaffman所提到的那样,回到默认的OpenSessionInViewFilter,因为您的实施显然无法解决您的问题。

答案 2 :(得分:1)

嗯,可能你不会在这个阶段改变你的ORM - 但它很好地提醒了为什么“Session-less”ORM很有趣。

e.g。 Ebean ORM ...没有会话,延迟加载正常工作(不需要过滤器) - 意味着你从未遇到过这个问题。

答案 3 :(得分:1)

只是一个想法:对我而言,它有助于改变我的过滤器的顺序。我有一个url重写过滤器和会话过滤器的顺序很重要。

只是一个想法...

答案 4 :(得分:0)

如果您的代码在控制器中失败,则过滤器是红色鲱鱼。您的Hibernate会话此时仍应打开,过滤或不过滤。

您可以发布您的DAO代码和相关配置吗?

答案 5 :(得分:0)

您说当您急切地加载集合对象时,您的应用程序运行正常。

由于您正在使用来自另一个请求的httpsession中的用户对象,因此您可能必须使用刷新或更新来重新附加用户对象。

以下代码是否有效?

User u = (User) session.getAttribute("currentUser");

User newUser = service.getUser(u.getUserId);
System.out.println(newUser.getBlogs());

看看这是否有帮助。你还能列出你的类路径中的jar文件。

<filter>
    <filter-name>openSessionInViewFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    <init-param>
        <param-name>singleSession</param-name>
        <param-value>true</param-value> <!-- or false -->
    </init-param>
    <init-param>
        <param-name>sessionFactoryBeanName</param-name>
        <param-value>sessionFactory</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>openSessionInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>