title: Spring/Hibernate @Transactional - Session closing before it should
body: <p>I can't figure out why, but the Hibernate session is closing before it should, so I can't fetch lazily loaded lists.</p>
  
  <p>In the logs, it's showing that the session closes immediately after hibernateTemplate.findByNamedParam() inside the DAO.</p>
  
  <p>When I run my web app, I get the following error:</p>
  
  <pre><code>Mar 29, 2011 3:13:21 PM org.apache.catalina.core.StandardWrapperValve invoke
  SEVERE: Servlet.service() for servlet [Spring MVC Dispatcher Servlet] in context with path [/apps] threw exception [Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.communitydriven.apps.entities.Project.tags, no session or session was closed] with root cause
  org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.communitydriven.apps.entities.Project.tags, 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.PersistentBag.iterator(PersistentBag.java:272)
      at com.communitydriven.apps.managers.ProjectManager.getProject(ProjectManager.java:98)
      at com.communitydriven.apps.controllers.ProjectController.getViewProject(ProjectController.java:86)
      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.invokeHandlerMethod(HandlerMethodInvoker.java:176)
      at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:426)
      at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:414)
      at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
      at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
      at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
      at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
      at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
      at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240)
      at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:164)
      at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:498)
      at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
      at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
      at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:562)
      at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
      at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:394)
      at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:243)
      at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:188)
      at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:166)
      at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302)
      at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
      at java.lang.Thread.run(Thread.java:619)
  </code></pre>
  
  <p>My DAO:</p>
  
  <pre><code>@Repository
  public class ProjectDao implements IProjectDao {
  
      private HibernateTemplate hibernateTemplate;
  
      @Autowired
      public void setSessionFactory(SessionFactory sessionFactory) {
          this.hibernateTemplate = new HibernateTemplate(sessionFactory);
      }
  
      ...
  
      /**
       * {@inheritDoc}
       */
      @Override
      public Project getProject(Project project) {
          // Validate required parameters
          if ( (project.getId() == null) ) {
              throw new NullPointerException("Missing required parameter: " + 
                      project.toString());
          }
  
          // Parameters
          List&lt;String&gt; paramNames = new ArrayList&lt;String&gt;();
          List&lt;Object&gt; values = new ArrayList&lt;Object&gt;();
  
          // Construct HQL
          StringBuffer hql = new StringBuffer();
          hql.append("from Project p ");
  
          boolean whereUsed = false; // track of "where" clause has been used
  
          // Filter by Id
          if (project.getId() != null) {
              whereUsed = DaoUtils.appendFilter(whereUsed, hql);
              hql.append("p.id = :id ");
              paramNames.add("id");
              values.add(project.getId());
          }
  
          // Get list of matching projects
          @SuppressWarnings("unchecked")
          List&lt;Project&gt; projects = 
              hibernateTemplate.findByNamedParam(
                      hql.toString(),
                      paramNames.toArray(new String[paramNames.size()]), 
                      values.toArray());
  
          // Get unique result
          Project projectResult = DataAccessUtils.uniqueResult(projects);
  
          return projectResult;
      }
  }
  </code></pre>
  
  <p>My Manager:</p>
  
  <pre><code>@Component
  public class ProjectManager implements IProjectManager {
  
      @Autowired
      private IProjectDao projectDao;
  
      ...
  
  
      /**
       * {@inheritDoc}
       */
      @Transactional
      @Override
      public ProjectMO getProject(Long projectId) {
          Project project = new Project();
          project.setId(projectId);
          project = projectDao.getProject(project);
  
          ProjectMO projectMO = new ProjectMO();
          projectMO.setId(project.getId());
          projectMO.setName(project.getName());
          projectMO.setDescription(project.getDescription());
  
          StringBuffer tags = new StringBuffer();
          final String DELIMITER = ", ";
          for (Tag tag : project.getTags()) {
              tags.append(tag.getName() + DELIMITER);
          }
          projectMO.setTags(tags.toString());
  
          return projectMO;
      }
  }
  </code></pre>
  
  <p>The Entity:</p>
  
  <pre><code>@Entity
  @Table
  public class Project {
      private Long id;
      private String name;
      private String description;
      private User submittedBy;
      private List&lt;Tag&gt; tags;
  
      public String toString() {
          final String DELIMITER = ", ";
          StringBuffer sb = new StringBuffer();
  
          sb.append(getClass().getName() + ": [");
          sb.append("id: " + id).append(DELIMITER);
          sb.append("name: " + name).append(DELIMITER);
          sb.append("description: " + description).append("]");
  
          return sb.toString();
      }
  
      // GETTERS AND SETTERS //
  
      @Id
      @Column
      @GeneratedValue
      public Long getId() {
          return id;
      }
      public void setId(Long id) {
          this.id = id;
      }
  
      @Column
      public String getName() {
          return name;
      }
      public void setName(String name) {
          this.name = name;
      }
  
      @Column
      public String getDescription() {
          return description;
      }
      public void setDescription(String description) {
          this.description = description;
      }
  
      @OneToOne
      @JoinColumn
      public User getSubmittedBy() {
          return submittedBy;
      }
  
      public void setSubmittedBy(User submittedBy) {
          this.submittedBy = submittedBy;
      }
  
      @ManyToMany
      @JoinTable(name="Projects_Tags", 
              joinColumns={@JoinColumn(name="project_id")}, 
              inverseJoinColumns={@JoinColumn(name="tag_id")})
      public List&lt;Tag&gt; getTags() {
          return tags;
      }
  
      public void setTags(List&lt;Tag&gt; tags) {
          this.tags = tags;
      }
  }
  </code></pre>
  
  <p>My Database context:</p>
  
  <pre><code>&lt;beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xsi:schemaLocation="
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 
   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"&gt;
  
      &lt;context:property-placeholder location="classpath:database.properties"/&gt;
  
      &lt;bean id="dataSource" 
               class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;
          &lt;property name="driverClassName" value="${jdbc.driverClassName}" /&gt;
          &lt;property name="url" value="${jdbc.url}" /&gt;
          &lt;property name="username" value="${jdbc.username}" /&gt;
          &lt;property name="password" value="${jdbc.password}" /&gt;
      &lt;/bean&gt;
  
      &lt;bean id="sessionFactory" 
          class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"&gt;
          &lt;property name="dataSource" ref="dataSource"/&gt;
          &lt;property name="hibernateProperties"&gt;
              &lt;value&gt;
                  hibernate.dialect=org.hibernate.dialect.MySQLDialect
                  hibernate.hbm2ddl.auto=update
              &lt;/value&gt;
          &lt;/property&gt;
          &lt;property name="packagesToScan" value="com.communitydriven.apps.entities" /&gt;
      &lt;/bean&gt;
  
      &lt;tx:annotation-driven /&gt;
  
      &lt;bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"&gt;
          &lt;property name="sessionFactory"&gt;&lt;ref local="sessionFactory"/&gt;&lt;/property&gt;
      &lt;/bean&gt;
  
  &lt;/beans&gt;
  </code></pre>