Hibernate lazy initialization exception
I am trying to create an application using spring mvc and hibenate. I have been seeing the exception failed to lazily initialize a collection of role
for nearly two days now :( The application runs fine if I eager load the collections. But I don't want it that way.
I tried implementing OpenSessionInViewFilter
in web.xml
but still the error persisted. I tried to extend OpenSessionInViewFilter
and use my own filter, even now the problem remains unsolved. Here is the filter I implemented
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);
}
}
}
I ran the application in debug mode. I find the session to be not null, and the closeSession gets invoked only after it passes through the controller code. But still if I try to fetch a collection in the controller when the session is open it fails :( Here is my 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>
What is going wrong?
As @sfussenegger mentioned...I am using sessions to identify the current user. This is my controller code:
@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";
}
and my jsp iterates over the MYblogs
I collected in the model
My controller code:
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";
}
exception raised:
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开发者_如何学Go.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)
Dick is correct, controller class might be doing something funny.
Another point is make sure you have all the required jar in your classpath such as cglib or javaassist.
It might be easier to use springs openSessionInViewInterceptor instead of OpenSessionInViewFilter (since you are already using spring).
simply add the following on your blog-servlet.xml config (you can of course split this to multiple files)
<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>
You need to add datasource on the configuration file and remove hibernateFilter from your web.xml.
Most likely you are using detached objects, i.e. object that have been loaded in a different session than the one created by your OpenSessionInViewFilter
. This might happen when you store objects in the session and access them from a subsequent request. Is this possible?
Edit:
I'd discourage keeping the User object in the session. Instead, only keep the id and fetch the User object from DB each time it's needed. Hence, instead of your current approach, you should use something like this
User getUserFromSession(HttpSession session) {
Integer userId = (Integer) session.getAttribute("currentUser");
return userId != null ? getObjectById(User.class, userId) : null;
}
Note that fetching an object by id is insanely fast, especially if you've configured Hibernate's second level cache to store User object - so forget about any performance considerations. The main advantage though is that you don't have to deal with detached objects anymore. Detached objects are evil and nobody likes them! ;)
And as @skaffman mentioned, go back to the default OpenSessionInViewFilter
as your implementation obviously won't solve your problem.
Hmmm, probably you are not going to change your ORM at this stage - but it is a nice reminder of why "Session-less" ORM's are interesting.
e.g. Ebean ORM ... no sessions, lazy loading just works (no filter required) - means you never hit this issue.
Just an idea: for me it helped to change the order of my filters. I had a url rewrite filter and the order with the session filter was important.
Just a thought...
If your code is failing in the controller, then the Filter is a red herring. Your Hibernate session should still be open at this point, filter or no filter.
Could you post your DAO code and the associated config?
You said that your application runs fine when you eagerly load the collection object.
Since you are using user object from the httpsession from another request, you might have to reattach the user object using refresh or update.
does the following code work?
User u = (User) session.getAttribute("currentUser");
User newUser = service.getUser(u.getUserId);
System.out.println(newUser.getBlogs());
And see if this helps. could you also list the jar files in your classpath.
<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>
精彩评论