我的SSH项目之旅(6.session问题解决)

本文详细探讨了Hibernate中Session关闭导致的问题及解决方案,包括利用Spring框架自动管理Session、配置OpenSessionInViewFilter等方法。
2008-07-20 22:25:27,531 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
2008-07-20 22:25:27,609 INFO [org.springframework.jdbc.support.SQLErrorCodesFact

ory] - SQLErrorCodes loaded: [DB2, HSQL, MS-SQL, MySQL, Oracle, Informix, PostgreSQL, Sybase]


2008-07-20 22:26:48,437 WARN [org.hibernate.jdbc.ConnectionManager] - finalizingwith closed connection
2008-07-20 22:26:48,437 WARN [org.hibernate.jdbc.ConnectionManager] - unclosed connection, forgot to call close() on your session?




Hibernate: select item0_.itemid as itemid, item0_.itemname as itemname1_, item0_.itemcode as itemcode1_ from item item0_
2008-07-22 20:54:56,375 WARN [org.hibernate.jdbc.ConnectionManager] – finalizing with closed connection


2008-07-22 20:56:32,812 WARN [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 0, SQLState: null
2008-07-22 20:56:32,812 ERROR [org.hibernate.util.JDBCExceptionReporter] - Cannot get a connection, pool error Timeout waiting for idle object
2008-07-22 20:56:32,828 WARN [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 0, SQLState: null
2008-07-22 20:56:32,828 ERROR [org.hibernate.util.JDBCExceptionReporter] - Cannot get a connection, pool error Timeout waiting for idle object
org.hibernate.exception.GenericJDBCException: Cannot open connection

终于找到解决办法了 ,我们来看下面的内容吧
ItemDAOImpl.java
public class ItemDAOImpl extends HibernateDaoSupport implements ItemDAO {

  public List queryAll() throws Exception {
    // TODO Auto-generated method stub
    Session session=super.getSession(true);
    String hql="from Item as i";
    List l=super.getSession().createQuery(hql).list();
    return l;
}
}\
其实上面的代码隐藏了一个问题,数据库连接并没有被关闭,所以一直出现以上的问题。
我这里提供三个解决方案
方案一:
用此种方法,虽然没有手动关闭数据库连接,但 spring 已经帮我们关闭了
return super.getHibernateTemplate().find(hql);
方案二:(经测试,此方案比较有效)
设定HibernateTemplateAllowCreateTrue
spring API HibernateDaoSupport
protected net.sf.hibernate.Session getSession(boolean allowCreate)
      Get a Hibernate Session, either from the current transaction or a new one.
public class ItemDAOImpl extends HibernateDaoSupport implements ItemDAO {

  public List queryAll() throws Exception {
    // TODO Auto-generated method stub
    Session session=super.getSession(true);
    String hql= "from Item as i" ;
      List l=session.createQuery(hql).list();
    try {
      return l;
    } finally {
    session.close();
    }
}
}
方案三:
Spring API:
geSession()org.springframework.orm.hibernate3.support.HibernateDaoSupport 中的一个方法,

它可以从当前事务或者一个新的事务获得一个hibernate 
session.
通常使用releaseSession(org.hibernate.Session)方法与getSession()配合。

如果没有绑定线程,releaseSession关闭由这个DAOSessionFactory创建的Hibernate Session。 
修改后的代码如下:

public class ItemDAOImpl extends HibernateDaoSupport implements ItemDAO {
  public List queryAll() throws Exception {
    // TODO Auto-generated method stub
    Session session = super.getSession();
    String hql = "from Item as i" ;
    List l = session.createQuery(hql).list();
    releaseSession(session);

  }
}
困扰了几天的问题终于解决了,项目搁浅了好几天了,就是对 springsession 的管理不清楚。

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: dj.fantlam.myssh.vo.Item.subitems - no session or session was closed

这是一个关于延迟加载的问题
applicationContext.xml加入
<bean id="hibernateInterceptor"
  class="org.springframework.orm.hibernate3.HibernateInterceptor">
    <property name="sessionFactory">
      <ref bean="sessionFactory" />
    </property>
  </bean>
在业务逻辑层中使用延迟加载
  
即使在视图外面, Spring 框架也通过使用 AOP 拦截器 HibernateInterceptor 来使得延迟加载变得很容易实现。这个 Hibernate 拦截器透明地将调用配置在 Spring 应用程序上下文中的业务对象中方法的请求拦截下来,在调用方法之前打开一个 Hibernate 会话,然后在方法执行完之后将会话关闭。



以及 Item.hbm.xml  
< set name = "subitems" inverse = "true" cascade="all” talbe=”subitem” 表示一对多 lazy="false" order-by="subcode " >
        < key >
          < column name = "itemid" />
        </ key >
        < one-to-many class = "dj.fantlam.myssh.vo.Subitem" />
    </ set >
问题得以解决

上网查了一些资料
hibernate一对多session关闭问题
  < filter >
    < filter-name > opensession </ filter-name >
    < filter-class >
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
    </ filter-class >
  </ filter >
  < filter-mapping >
    < filter-name > opensession </ filter-name >
    < url-pattern > *.do </ url-pattern >
  </ filter-mapping >
openSessionInView 的意思就是延长 session 的生命周期,到了 jsp 层, session 依然有效,所以就可以正确读取一对多的多方的属性值了

2008-07-25 21:47:31,031 ERROR [org.apache.catalina.core.ContainerBase.[Catalina]
.[localhost].[/myssh].[jsp]] - Servlet.service() for servlet jsp threw exception

java.lang.IllegalStateException: getOutputStream() has already been called for this response

原因是image.jsp的问题   由于jsp container在处理完成请求后会调用releasePageContet方法释放所用的PageContext object,并且同时调用getWriter方法,由于getWriter方法与在jsp页面中使用流相关的getOutputStream方法冲突,所以会造成这种异常,

解决方法 在最后加上

out.clear();
out = pageContext.pushBody();


Question.hbm.xml
  < set name = "answers" inverse = "true" table="answer" >
        < key >
          < column name = "qid" />
        </ key >
        < one-to-many class = "dj.fantlam.myssh.vo.Answer" />
    </ set >




刚刚在 http://localhost:8888/myssh/jsp/show.do?qid=6&status=selectid 这块出现了延迟加载的问题,也就是 question_show.jsp
  < logic:iterate id = "ans" name = "question" property = "answers" >
  </ logic:iterate > answers 的时候没读到
然后我在 Question.hbm.xml 加上 lazy="false"就行了
< set name = "answers" inverse = "true" table = "answer" lazy="false" >
        < key >
          < column name = "qid" />
        </ key >
        < one-to-many class = "dj.fantlam.myssh.vo.Answer" />
</ set >


今天我又发现了一个问题,原来之前的解决方案是多余的。我就是觉得很奇怪,前段日子就是怎么测都是 session 关闭了,真的很莫名其妙。实际上 OpenSessionInViewFilter 这个配置就是解决在 websession 的问题,而之前我配了之后不起作用,现在想想原因应该是 tomcat 里的项目之间的干扰吧,因为如今我把一些别的项目去掉了,问题就没了(不过当时我是有在虚拟机做测试的,一个新的 tomcat 也同样出问题,百思不得其解,再测试一下再说吧,以后有问题再补充)。害得我走了冤枉路,还到论坛里问了许久。总结一下,就是利用 hibernateTemplate 或者是直接利用 hibernateDAOSupport 类来取得 session 这两种方法都可以自动处理 session 的,前一种是 spring 机制,而后一种是 hibernate 机制。
2008-08-06 17:15:13,272 INFO [org.springframework.orm.hibernate3.support.OpenSessionInViewFilter] - Initializing filter 'opensession'
2008-08-06 17:15:13,444 INFO [org.springframework.core.CollectionFactory] - JDK 1.4+ collections available
2008-08-06 17:15:13,475 INFO [org.springframework.core.CollectionFactory] - Commons Collections 3.x available
2008-08-06 17:15:13,490 INFO [org.springframework.orm.hibernate3.support.OpenSessionInViewFilter] - Filter 'opensession' configured successfully
经过测试 不配置OpenSessionInViewFilter的话在网页读取的时候就会发生session问题



网上有这么一种说法
---------------------------------------------------------------------------------------------------
strutsspring的整合是在struts-config.xml里加了个plugin

< plug-in
  className = "org.springframework.web.struts.ContextLoaderPlugIn" >
    < set-property property = "contextConfigLocation"
      value = "/WEB-INF/classes/applicationContext.xml" />
  </ plug-in >
这一段必须去掉
web.xml 加上
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
保证 openSessionInView 的正常使用
经过我的测试,此方法有道理。我们先来看看spring整合struts的方法吧。两者整合的一种通用配置:在web.xml中加入一下任意一种
1
  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>

2
  <servlet>
    <servlet-name>context</servlet-name>
    <servlet-class>
    org.springframework.web.context.ContextLoaderServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

为了让 spring ApplicationContext 实例化,作为 IOC 容器,必须优先加载,一种常见的做法是在 web.xml 加载 spring 容器。所以以上 2 种方法都是一样的, ContextLoaderListenerContextLoaderServlet 底层都依赖 ContextLoader ,因此两者效果几乎没有什么区别。在 Servlet2.3 标准中才出现 ContextLoaderListener ,它比 ContextLoaderServlet 会优先加载。

现在我得出的结论是之前是 OpenSessionInViewFilter 不起作用,如果我不把 plugin
去掉的话问题依旧。经过测试,我不加 plugin问题就没了。(若以后有相关问题再继续讨论)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值