Hibernate/Spring3:无法初始化代理 - 没有会话

5
我刚开始学习Spring,所以我在我的Mac上将Spring 3.0M3放在Tomcat服务器上,创建了一个Eclipse项目,完成了一个Hello World,然后想用Hibernate使对象持久化。我已经在我的MySQL服务器上创建了一个用户表,制作了一个具有所有getter和setter的用户对象(我真希望Java能从Objective-C中获取动态属性并添加到代码生成的属性代码中,这样可以减少很多代码冗余),制作了一个UserDao对象来查找和保存用户,并制作了bean配置。一切都进行得相当顺利......除了会话未初始化之外。为什么会这样?我可以使用这些凭据从这台计算机轻松访问数据库。
我意识到这可能只是普通的初学者问题,但在谷歌上搜索错误时,我发现的所有内容都是那些在从Hibernate 2转换到3时在中途失去会话的人。我没有转换,据我所知,会话从未建立。
这是我的错误:
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)

这是我相关bean的servlet配置:
<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>

这是我的User类:

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;
    }
}

这里是我的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;
    }
}

这是我的UserService接口。
public interface UserService {

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

及其实现:

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);
    }
}

最后,这是我的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;
  }

}

我希望你能给我一些有用的技巧 :-) 如果你觉得代码中还有其他地方不符合Spring框架的规范,也请告诉我。

谢谢

Nik
2个回答

8
您的堆栈跟您发布的示例代码不相符。堆栈显示您正在从调用,但是您的示例代码并没有这样做(也不应该这样做,因为不应在此处调用)。
编辑:好的,有了新的堆栈跟踪,问题似乎很简单,您正在调用,这只应在特定情况下使用(而这不是其中之一)。您可能应该调用。

哎呀,我在写问题的时候尝试清理了一下。让我再次崩溃并更新堆栈跟踪 :-) - niklassaers
啊哈!非常感谢!!!这本书(我必须补充说非常好),《Spring实战》第190页明确提到了.load(),所以我使用了它。也许自从写这本书以来已经有所改变。现在.load()是什么意思/与.get()有何不同?现在一切都运作得非常好。非常非常感谢 :-) - niklassaers
2
我尝试过了,但 hibernate.org 没有响应。 - niklassaers
这些天它非常不稳定。请振作起来,红帽公司。 - skaffman

3
当你进行dao调用时,你会“加载”对象。加载不会触发数据库操作,它会返回一个代理对象,只有在绝对必要的情况下才会触发数据库操作。
在加载之前,你没有启动事务——加载工作是因为你已经打开了非事务性读取(默认情况下打开)。它不会说:“抱歉,没有事务”(它基本上会为你创建一个事务来执行读取操作,该事务立即结束)。
因此,当实际需要访问数据库时执行getName方法时,它会告诉你“没有会话”,因为加载该对象的会话已关闭(它立即关闭了)。
要解决这个问题,你需要在servlet调用开始时启动事务,并在结束时结束事务——这样当你执行getName方法时,会话仍然可用。
可能在Spring中有一些可以处理这个流程的东西。

那么你的意思是我不能使用非事务性流程?其次,我认为Spring会为我处理这个问题。毕竟,它有数据源、sessionFactory和实体吧?第三,我已经关闭了延迟获取,所以它不应该只创建代理对象,而应该填充它,对吧? - niklassaers
现在我已经更新了我的问题,包括可以在事务中使用的服务,并包含了一个事务处理程序。然而,我仍然得到完全相同的错误:没有会话。我该如何启动此会话?将其放入事务中似乎没有帮助。 - niklassaers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接