我正在尝试使用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)
答案 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)
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>