tags:

views:

763

answers:

8

I am trying to create an application using spring mvc and hibenate.I have been seeing this 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 dont 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 tried 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>

Please if some body could help me i will be really thankful..I dont know what is going wrong

+1  A: 

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.

sfussenegger
Ya u are right.. i am using session to identity the current user...But initially i went only with the default OpenSessionViewFilter approach..But even that didn't help
prabha
you're right, the root of all evil isn't early optimization but detached objects!
msparer
A: 

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 blogs = new ArrayList(u.getBlogs());

model.addAttribute("myBlogs", blogs);

}

return "show"; }

and my jsp iterate over the MYblogs i collected in the model

prabha
please see the edit version of my answer
sfussenegger
A: 

I am very thankful to u for guiding me..But i didn't get the output this time too..I implemented the default OpenSessionInViewFilter, I passed the id in the httpsession found the user object and tried to fetch the collection..But this time too it is raising lazy exception.I never opend any session or transaction in my service classes..Sorry if i am bothering u a lot

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.(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)

prabha
Looks like now it's getting tricky - I'd set some breakpoints in `OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)` and `AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:365)` to check whether both are using the same session.
sfussenegger
A: 

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?

Dick Chesterwood
If it is true that your code is failing at the point where you have added the comment, the hibernate session must be being closed in the call to findById in your DAO. I haven't seen your DAO code yet, please post this.
Dick Chesterwood
+1  A: 

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.

surajz
Hey thanks a lot :) I removed the filter and implemented the interceptor..Now it works fine..Thanks Thanks Thanks
prabha
That's great. Interceptor is a better way to go because you can test it outside your web class..
surajz
A: 

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>
surajz
A: 

Hey, I removed the filter and implemented the interceptor..Now my app works fine without eager loading...But I need to dig the issue with the filter though :D Anyway thanks a lot..I was really struggling to make my app work without eager loading :)

prabha
A: 

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.

Rob