could not initialize proxy - no Session 异常情况探究

近期跟着“尚学堂”马士兵的spring教学视频复习了一番,学到45课的时候,得知了这样一个据说经典的异常:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session


此异常产生的原因经过马老师以及我多方的查阅可以总结如下:

由于在数据库获取数据采用的是load的方法 只是一个代理对象  则jsp页面用到相关数据才用发出sql语句对数据库进行查询 但等到jsp页面需要数据发出sql语句 这时session已经关了 ,因此会报出异常。 Spring为此提供了一个拦截器 在jsp页面查询到数据后再将session关闭。


解决方案,个人觉着在这个地方比较有价值的有两个,也正如马老师所说的:

1.将dao层中的load方法改为get方法;

2.在web.xml文件中配置拦截器,主要代码如下:

使用OpenSessionInViewFilter。这种方法是将session交给servlet filter来管理,每当一个请求来之后就会开

启一个session,只有当响应结束后才会关闭。如下:

[html]  view plain copy print ?
  1. <filter-name>hibernateFilter</filter-name>   
  2.      <filter-class>  org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </filter-class>   
  3. </filter   
  4. <filter-mapping>   
  5.      <filter-name>hibernateFilter</filter-name>   
  6.      <url-pattern>/*</url-pattern>   
  7. </filter-mapping>   

此处普遍强调一个,就是这段过滤器需要配置在同在web.xml文件中的struts2过滤器代码之前,否则无效。

详细情况可以谷歌“org.hibernate.LazyInitializationException: could not initialize proxy - no Session”这个异常,文章超多,此处不再复制粘贴。




如果真有那么顺利,我也不用记录这篇博文了。问题就来了,我按照课程及各类高手的博客中讲的在web.xml文件中按要求加入了上面的过滤器,位置也绝对放对了,但是没有生效,四下“谷歌”也没有得出什么线索,虽然也有人遇到这种情况,但都是顺序放错导致的。


没有办法之下,讲马士兵的源码部署起来,居然可以,于是思考是不是jar包版本不一样,所以不行。事实证明和jar包半点关系都没有。

再次猜测是不是项目不是新建的,而是通过拷贝粘贴改改导致的,于是新建项目,然后将代码考入,再次把之前的业务代码拷贝粘贴一遍,依然不行,证明这个猜测也不对。

于是再次把马老师的业务代码考进来,离奇的事就是居然可以。仔细对比,终于找着唯一的不同:

@Component("u")
@Scope("prototype")

public class UserAction extends ActionSupport implements ModelDriven<UserRegisterInfo>{


/**
* 采用DTO对象封装表单请求数据
*/
private static final long serialVersionUID = 1L;

private UserRegisterInfo urinfo = new UserRegisterInfo();//DTO对象


private UserManager userManager;

private List<User> userList;

private User userInfo;



public UserAction(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
userManager = (UserManager) ac.getBean("userManager");
}


/**
* @return the userManager
*/
public UserManager getUserManager() {
return userManager;
}
/**
* @param userManager the userManager to set
*/

public void setUserManager(UserManager userManager) {
this.userManager = userManager;
}



public String execute() throws Exception {
User user = new User();
user.setName(urinfo.getUsername());
user.setPassword(urinfo.getUserpass1());
if(userManager.exists(user)){
return "fail";
}
userManager.add(user);
return "success";
}

/**
* 列出所有用户信息
* @return
* @throws Exception
*/
public String list() throws Exception{
userList = userManager.getAllUserList();
return "list";
}

以上是我配置的action,通过spring注解管理。


<struts>


<package name="registration" extends="struts-default">

<action name="user" class="u">
<!-- com.inta.action.UserAction -->
<result name="success">/registerSuccess.jsp</result>
<result name="fail">/registerFail.jsp</result>
<result name="list">/userList.jsp</result>
<result name="load">/userInfo.jsp</result>
</action>

</package>


</struts>

上面是struts.xml配置文件。




两处着色的区域就是不同点,于是将两处地方前者注释掉,后者改为action的类名路径:com.inta.action.UserAction

再次部署,一切OK。到这里,恍然大悟。其实在马老师第42可时候已经分析过这个问题,只是讲的不是很突出重点,不仔细想就过去了。

问题1在于,SSH框架整合时,struts2是主导,struts2通过和spring的插件struts2-spring-plugin.来管理spring,spirng的作用依然是容器,负责管理各个service层和dao层的bean,但struts2内部本身也有一个容器,会管理action中的各个实例变量,所以即使action上的注解全部去掉,表面上看没有让spring管理起来,其实依然会被实例化成bean,并且是按name方式管理。此时即使在action中为各个实例变量加上注释看起来像用spring管理,其实并不会生效,aciton中什么注释也不用都可以。问题2在于,struts.xml文件那里,如果按我上面的方法所写,意味着user这个anciton指向的是spring管理那个name叫u的bean,但正如马老师课程所说,struts2自己会管理action,如果多余手动注释再去管理action就会产生两个bean,于是问题来了,两个action的bean中指向的session不是同一个,所以按我上面的配置写法,即使通过过滤器扩大了session的会话范围,但是根本没有作用到struts2自己管理的session上来,所以根本不会起作用,只有将struts.xml中的 action用类的完整名,才可以定位到struts2管理的bean上来,过滤器配置才会生效。



补充一点:

如果非要将action的管理全权交给spring来做,事实上工作中也都是这么做的,那可以加回上述注释部分,然后将struts.xml文件中的class改为注释配置的name,此处name为u,因此改为:<action name="user" class="u">

这样一改,action类中的所有实例变量就不会有sturts2来管理,必须由spirng注入,否则就会包上述异常。因此在action中的

public void setUserManager(UserManager userManager) {
this.userManager = userManager;
}

前就需要加注释@resource,这样才完全将action交由spring管理。

你可能感兴趣的:(spring,Hibernate,struts2,session,异常)