views:

2817

answers:

2

Hi guys, I'm new at Spring, so I put Spring 3.0M3 on a Tomcat server on my Mac, made an Eclipse project, completed a Hello World and then wanted to make an object persist with Hibernate. I've made a User table on my MySQL server, made a User object with all getters and setters (I really wish Java would take a queue from Objective-C here and add dynamic properties. Too much code clutter with generated property-code), made a UserDao object to look up and save a User, and made the bean config. It goes fairly well... except for that the session isn't initialized. Why is this? I can access the database just fine from this computer using those credentials.

I realize that this is probably just normal beginner stuff, but all I've found on the error while googling has been people that are loosing the sessions mid-way when transitioning from Hibernate 2 to 3. I'm not transitioning, and as far as I can tell the session is never made.

Here's my error:

SEVERE: Servlet.service() for servlet HelloApp threw exception
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:57)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
    at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)
    at com.saers.data.entities.User$$EnhancerByCGLIB$$c2f16afd.getName(<generated>)
    at com.saers.view.web.controller.HelloWorldController.handleRequestInternal(HelloWorldController.java:22)
    at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153)
    at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:48)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:763)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:709)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:613)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:525)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
    at java.lang.Thread.run(Thread.java:637)

Here is my servlet config for the related beans:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName"   value="com.mysql.jdbc.Driver" />
    <property name="url"     value="jdbc:mysql://10.0.0.3:3306/HelloDB" />
    <property name="username" value="hello" />
    <property name="password" value="w0rld" />
    <property name="initialSize" value="2" />
    <property name="maxActive" value="5" />
  </bean>

  <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="annotatedClasses">
      <list>
        <value>com.saers.data.entities.User</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
        <prop key="hibernate.show_sql">true</prop>
        <prop key="hibernate.lazy">false</prop>
      </props>
    </property>
  </bean>

  <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
  </bean>

  <bean id="userDAO" class="com.saers.data.dao.UserDao">
    <property name="sessionFactory" ref="sessionFactory"/>
  </bean>

  <bean id="txProxyTemplate" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
    <property name="transactionManager" ref="transactionManager"/>
    <property name="transactionAttributes">
      <props>
        <prop key="add*">PROPAGATION_REQUIRED</prop>
        <prop key="update*">PROPAGATION_REQUIRED</prop>
        <prop key="delete*">PROPAGATION_REQUIRED</prop>
        <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
      </props>
    </property>
  </bean>

  <bean id="userService" parent="txProxyTemplate">
    <property name="target">
      <bean class="com.saers.business.UserServiceImpl">
        <property name="userDao" ref="userDAO"/>
      </bean>
    </property>
    <property name="proxyInterfaces" value="com.saers.business.UserService"/>
  </bean>

  <bean name="/" class="com.saers.view.web.controller.HelloWorldController">
    <property name="userService" ref="userService"/> 
  </bean>

Here is my User class:

package com.saers.data.entities;

import java.util.Date;
import java.io.Serializable;

import javax.persistence.*;

@Entity
@Table(name = "User")
public class User implements Serializable {

    private static final long serialVersionUID = -6123654414341191669L;

    @Id
    @Column(name = "WebUserId")
    private String WebUserId;

    @Column(name = "Name")
    private String Name;
    /**
     * @return the webUserId
     */
    public synchronized String getWebUserId() {
     return WebUserId;
    }
    /**
     * @param webUserId the webUserId to set
     */
    public synchronized void setWebUserId(String webUserId) {
     WebUserId = webUserId;
    }
    /**
     * @return the name
     */
    public synchronized String getName() {
     return Name;
    }
    /**
     * @param name the name to set
     */
    public synchronized void setName(String name) {
     Name = name;
    }
}

And here is my UserDao:

package com.saers.data.dao;

import java.util.List;

import com.saers.data.entities.User;

import org.springframework.orm.hibernate3.support.*;


public class UserDao extends HibernateDaoSupport {

    public void saveUser(User user) {
     getHibernateTemplate().saveOrUpdate(user);
    }

    public User lookupUser(String WebUserId) {
     User user = getHibernateTemplate().load(User.class, WebUserId);
     return user;
        return user;
    }
}

Here's my UserService interface

public interface UserService {

    public User lookupUser(String webUserId);
    public void setUserDao(UserDao userDao);
}

and its implementation:

public class UserServiceImpl implements UserService {

    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
     this.userDao = userDao;
    }

    @Override
    public User lookupUser(String webUserId) {

     return userDao.lookupUser(webUserId);
    }
}

And finally, here's my HelloWorldController:

package com.saers.view.web.controller;

import com.saers.data.entities.User;
import com.saers.business.UserService;

import org.springframework.web.servlet.mvc.*;
import org.springframework.web.servlet.*;
import javax.servlet.http.*;
import org.apache.commons.logging.*;



public class HelloWorldController extends AbstractController {

  protected final Log logger = LogFactory.getLog(getClass());

  @Override
  public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {

    logger.info("Get bean");
    User user = userService.lookupUser("helloUser");
    logger.info("Found out that this user was last changed " + user.getName());
    logger.info("Return View");
    ModelAndView mv = new ModelAndView("HelloWorld.jsp", "user", user);
    return mv;
  }

  private UserService userService = null;
  public void setUserService(UserService userService) {
      this.userService = userService;
  }

}

I hope you have some good tips I can use :-) If there's anything else in the code you feel that I'm doing the wrong/non-spring way, I would love to hear about that as well.

Cheers

Nik
+5  A: 

Your stack trace does not match up with the sample code you posted for UserDao. The trace says that you're calling HibernateTemplate.initialize() from UserDao.lookupUser(), but your sample code is doing no such thing (as well it shouldn't, initialize() should not be called here).

Edit: OK, with the new stack trace, it seems the problem is simply that you're calling hibernateTemplate.load(), which should only be used in specific circumstances (and this isn't one of them). You should probably be calling hibernateTemplate.get().

skaffman
Oops, I tried cleaning a bit up while writing the question. Let me crash it again and update the stacktrace :-)
niklassaers
Stacktrace updated :-)
niklassaers
Aha!!! Thank you *VERY* much!!!! The book (quite excelent I have to add), Spring in Actino on page 190 said .load() explicitly, so I used that. Perhaps this has been changed since the book was written. What does .load() mean now/how is it different from .get()? Now everything works super. Thank you VERY VERY much :-)
niklassaers
jag (just ask google)
Michael Wiles
I did, hibernate.org wasn't responding
niklassaers
It's very flaky these days. Get your act together, redhat.
skaffman
+2  A: 

When you do the dao call, you "load" the object. Load does not hit the database, it will return a proxy onto the object so that the database is only hit when absolutely necessary.

You did not start a transaction before the load - the load works because you have non transactional reads turned on (it's on by default). It doesn't say, sorry, no transaction (it basically creates one for you to do the read, which ends immediately).

Thus when you do the getName when it actually has to hit the database it tells you "no session" because the session which did load that object is closed (it was closed immediately).

What you need to do to fix it is start the transaction at the beginning of the servlet call and end it at the end - that way when you do the getName the session will still be available.

There's probably something in spring which will handle this plumbing for you.

Michael Wiles
So what you say is that I cannot use a non-transactional flow?Secondly, I was under the impression that Spring would handle this for me. After all, it has the datasource, the sessionFactory and the entity?Thirdly, I've turned off lazy fetching, so it shouldn't just create the proxy object, it should populate it too, right?
niklassaers
Now I've updated my question to include a service that can be used in a transaction and included a transaction handler. However, I still get the exact same error: no Session. How do I get this session started? Putting it in a transaction didn't seem to help
niklassaers