继承 HibernateDaoSupport 的getSession 问题

来源:互联网 发布:淘宝外卖怎么增加口袋 编辑:程序博客网 时间:2024/05/16 04:53

这段时间遇到一个问题,接收的项目Dao继承的是 HibernateDaoSupport 且没有加上事务 在进行后期测试的时间发现web服务器内存爆掉了,后来检测了一下session的开启数,发现有很多session没有关掉,因为这个问题就到网上查了资料,网上大部分的解决如下

 

1.重写 HibernateDaoSupport用ThreadLocal保存Session列表并编写一个配套的过滤器来显式关闭Session, 并在关闭之前尝试提交事务. 下面是重写的 HibernateDaoSupport 代码:

 

package org.springframework.orm.hibernate3.support;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.support.DaoSupport;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.SessionFactoryUtils;


/**
* 修改后的避免连接泄漏的 HibernateDaoSupport, 多连接版本, 不保证跨DAO的事务.
*
* @author 刘长炯
* Date: 2009-3-11
*/
publicabstractclass HibernateDaoSupport extends DaoSupport {
/** 使用 ThreadLocal 保存打开的 Session 列表 */
privatestaticfinal ThreadLocal< List<Session> > sessions = new ThreadLocal< List<Session> >();

/**
     * 获取Hibernate连接.
     * @return
     */
publicstatic List<Session> getSessionList() {
//1. 先看看是否有了List get()
        List list = sessions.get();
// 2. 没有的话从创建一个, put()
if(list == null) {
            list = new ArrayList();
            sessions.set(list);
        }
// 3. 返回 Session
return list;
    }

/**
     * 关闭当前线程中未正常释放的 Session.
     */
publicstaticvoid closeSessionList() {
//        1. 先看看是否有了List get()
        List<Session> list = sessions.get();
// 2. 有的话就直接关闭
if(list != null) {
            System.out.println(SimpleDateFormat.getDateTimeInstance().format(new java.util.Date()) +
" -------- 即将释放未正常关闭的 Session");


for(Session session : list) {
                System.out.println("正在关闭 session =" + session.hashCode());
// ! 关闭前事务提交
if(session.isOpen()) {
try {
                        session.getTransaction().commit();
                    } catch(Exception ex) {
try {
                            session.getTransaction().rollback();
                        } catch (HibernateException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
                        }
                    }
try {
                        session.close();
                    } catch(Exception ex) {

                    }

                }
//releaseSession(session); // 无法调用
            }
            sessions.remove();
        }

    }

private HibernateTemplate hibernateTemplate;


/**
     * Set the Hibernate SessionFactory to be used by this DAO.
     * Will automatically create a HibernateTemplate for the given SessionFactory.
     * @see #createHibernateTemplate
     * @see #setHibernateTemplate
     */
publicfinalvoid setSessionFactory(SessionFactory sessionFactory) {
if (this.hibernateTemplate == null || sessionFactory != this.hibernateTemplate.getSessionFactory()) {
this.hibernateTemplate = createHibernateTemplate(sessionFactory);
        }
    }

/**
     * Create a HibernateTemplate for the given SessionFactory.
     * Only invoked if populating the DAO with a SessionFactory reference!
     * <p>Can be overridden in subclasses to provide a HibernateTemplate instance
     * with different configuration, or a custom HibernateTemplate subclass.
     * @param sessionFactory the Hibernate SessionFactory to create a HibernateTemplate for
     * @return the new HibernateTemplate instance
     * @see #setSessionFactory
     */
protected HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) {
returnnew HibernateTemplate(sessionFactory);
    }

/**
     * Return the Hibernate SessionFactory used by this DAO.
     */
publicfinal SessionFactory getSessionFactory() {
return (this.hibernateTemplate != null ? this.hibernateTemplate.getSessionFactory() : null);
    }

/**
     * Set the HibernateTemplate for this DAO explicitly,
     * as an alternative to specifying a SessionFactory.
     * @see #setSessionFactory
     */
publicfinalvoid setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
    }

/**
     * Return the HibernateTemplate for this DAO,
     * pre-initialized with the SessionFactory or set explicitly.
     * <p><b>Note: The returned HibernateTemplate is a shared instance.</b>
     * You may introspect its configuration, but not modify the configuration
     * (other than from within an {@link #initDao} implementation).
     * Consider creating a custom HibernateTemplate instance via
     * <code>new HibernateTemplate(getSessionFactory())</code>, in which
     * case you're allowed to customize the settings on the resulting instance.
     */
publicfinal HibernateTemplate getHibernateTemplate() {
returnthis.hibernateTemplate;
    }

protectedfinalvoid checkDaoConfig() {
if (this.hibernateTemplate == null) {
thrownew IllegalArgumentException("'sessionFactory' or 'hibernateTemplate' is required");
        }
    }


/**
     * Obtain a Hibernate Session, either from the current transaction or
     * a new one. The latter is only allowed if the
     * {@link org.springframework.orm.hibernate3.HibernateTemplate#setAllowCreate "allowCreate"}
     * setting of this bean's {@link #setHibernateTemplate HibernateTemplate} is "true".
     * <p><b>Note that this is not meant to be invoked from HibernateTemplate code
     * but rather just in plain Hibernate code.</b> Either rely on a thread-bound
     * Session or use it in combination with {@link #releaseSession}.
     * <p>In general, it is recommended to use HibernateTemplate, either with
     * the provided convenience operations or with a custom HibernateCallback
     * that provides you with a Session to work on. HibernateTemplate will care
     * for all resource management and for proper exception conversion.
     * @return the Hibernate Session
     * @throws DataAccessResourceFailureException if the Session couldn't be created
     * @throws IllegalStateException if no thread-bound Session found and allowCreate=false
     * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
     */
protectedfinal Session getSession()
throws DataAccessResourceFailureException, IllegalStateException {
        Session session = getSession(this.hibernateTemplate.isAllowCreate());

// 开始事务
try {
            session.beginTransaction();
        } catch (HibernateException e) {
            e.printStackTrace();
        }

        getSessionList().add(session);

return session;
    }

/**
     * Obtain a Hibernate Session, either from the current transaction or
     * a new one. The latter is only allowed if "allowCreate" is true.
     * <p><b>Note that this is not meant to be invoked from HibernateTemplate code
     * but rather just in plain Hibernate code.</b> Either rely on a thread-bound
     * Session or use it in combination with {@link #releaseSession}.
     * <p>In general, it is recommended to use
     * {@link #getHibernateTemplate() HibernateTemplate}, either with
     * the provided convenience operations or with a custom
     * {@link org.springframework.orm.hibernate3.HibernateCallback} that
     * provides you with a Session to work on. HibernateTemplate will care
     * for all resource management and for proper exception conversion.
     * @param allowCreate if a non-transactional Session should be created when no
     * transactional Session can be found for the current thread
     * @return the Hibernate Session
     * @throws DataAccessResourceFailureException if the Session couldn't be created
     * @throws IllegalStateException if no thread-bound Session found and allowCreate=false
     * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
     */
protectedfinal Session getSession(boolean allowCreate)
throws DataAccessResourceFailureException, IllegalStateException {

return  (!allowCreate ?
            SessionFactoryUtils.getSession(getSessionFactory(), false) :
                SessionFactoryUtils.getSession(
                        getSessionFactory(),
this.hibernateTemplate.getEntityInterceptor(),
this.hibernateTemplate.getJdbcExceptionTranslator()));
    }

/**
     * Convert the given HibernateException to an appropriate exception from the
     * <code>org.springframework.dao</code> hierarchy. Will automatically detect
     * wrapped SQLExceptions and convert them accordingly.
     * <p>Delegates to the
     * {@link org.springframework.orm.hibernate3.HibernateTemplate#convertHibernateAccessException}
     * method of this DAO's HibernateTemplate.
     * <p>Typically used in plain Hibernate code, in combination with
     * {@link #getSession} and {@link #releaseSession}.
     * @param ex HibernateException that occured
     * @return the corresponding DataAccessException instance
     * @see org.springframework.orm.hibernate3.SessionFactoryUtils#convertHibernateAccessException
     */
protectedfinal DataAccessException convertHibernateAccessException(HibernateException ex) {
returnthis.hibernateTemplate.convertHibernateAccessException(ex);
    }

/**
     * Close the given Hibernate Session, created via this DAO's SessionFactory,
     * if it isn't bound to the thread (i.e. isn't a transactional Session).
     * <p>Typically used in plain Hibernate code, in combination with
     * {@link #getSession} and {@link #convertHibernateAccessException}.
     * @param session the Session to close
     * @see org.springframework.orm.hibernate3.SessionFactoryUtils#releaseSession
     */
protectedfinalvoid releaseSession(Session session) {
        SessionFactoryUtils.releaseSession(session, getSessionFactory());
    }

}

用这个类来覆盖Spring内置的那个HibernateDaoSupport, 然后随便编写一个过滤器, 如下所示:
publicvoid doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {

req.setCharacterEncoding(this.charset);
chain.doFilter(req, res);
// 关闭遗漏的 Session
HibernateDaoSupport.closeSessionList();
}

 

经过实地的测试了一下,此种方法虽然能关闭有些session但会把所有已经开启的session全部关掉,大家总不希望开发的项目只能一个人进行操作吧。

 

万般无奈只能另辟蹊径了,因为HibernateDaoSupport 的 getSession() 实际调用的 是 getOpenSession 每次调用它都会new 一个session 导致了session的频繁创建,后来想假如每次进行http请求先判断一下 此线程是否已经创建了session如果创建了就调用本线程对应的session(getCurrentSession()),如果没有就 openSession 赋给此线程,再在操作结束时统一关闭此session,具体实现如下:

重写的HibernateDaoSupport

 

/*
 * Copyright 2002-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.orm.hibernate3.support;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.support.DaoSupport;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.SessionFactoryUtils;

/**
 * Convenient super class for Hibernate-based data access objects.
 *
 * <p>Requires a {@link org.hibernate.SessionFactory} to be set, providing a
 * {@link org.springframework.orm.hibernate3.HibernateTemplate} based on it to
 * subclasses through the {@link #getHibernateTemplate()} method.
 * Can alternatively be initialized directly with a HibernateTemplate,
 * in order to reuse the latter's settings such as the SessionFactory,
 * exception translator, flush mode, etc.
 *
 * <p>This base class is mainly intended for HibernateTemplate usage but can
 * also be used when working with a Hibernate Session directly, for example
 * when relying on transactional Sessions. Convenience {@link #getSession}
 * and {@link #releaseSession} methods are provided for that usage style.
 *
 * <p>This class will create its own HibernateTemplate instance if a SessionFactory
 * is passed in. The "allowCreate" flag on that HibernateTemplate will be "true"
 * by default. A custom HibernateTemplate instance can be used through overriding
 * {@link #createHibernateTemplate}.
 *
 * @author fanpj 

 * @since 1.2
 * @see #setSessionFactory
 * @see #getHibernateTemplate
 * @see org.springframework.orm.hibernate3.HibernateTemplate
 */
public abstract class HibernateDaoSupport extends DaoSupport {

 

 private HibernateTemplate hibernateTemplate;
 private static final ThreadLocal sessionThread = new ThreadLocal();
 private static final ThreadLocal transactionThread = new ThreadLocal();
 private static SessionFactory sessionFactory1 = null;

 

 /**
  * Set the Hibernate SessionFactory to be used by this DAO.
  * Will automatically create a HibernateTemplate for the given SessionFactory.
  * @see #createHibernateTemplate
  * @see #setHibernateTemplate
  */
 public final void setSessionFactory(SessionFactory sessionFactory) {
  if (this.hibernateTemplate == null || sessionFactory != this.hibernateTemplate.getSessionFactory()) {
   this.hibernateTemplate = createHibernateTemplate(sessionFactory);
  }
  if(sessionFactory1==null)
  {
   sessionFactory1=sessionFactory;
  }
 }
 

 /**

     * 获取当前线程绑定的session

     *

     * @return Session

     * @throws HibernateException

     */

    public static Session getSession() {

        Session s = (Session) sessionThread.get();

        if (s == null) {

            s = sessionFactory1.openSession();

            sessionThread.set(s);

        }
        return s;
    }
   

/**

 *获取当前线程绑定的transaction

*/
    public static Transaction transaction() {

        Transaction transaction = (Transaction) transactionThread.get();

        if (transaction == null) {

            transaction = getSession().beginTransaction();

            transactionThread.set(transaction);

        }

        return transaction;

    }

    /**

     * 提交当前session的事务

     */

    public static void commitTransaction() {

        Transaction transaction = (Transaction) transactionThread.get();

        transactionThread.set(null);
        if (transaction != null)

            transaction.commit();

    }

 

    /**

     * 回滚当前session的事务

     */

    public static void rollbackTransaction() {

        Transaction tx = (Transaction) transactionThread.get();

        transactionThread.set(null);

        if (tx != null)

            tx.rollback();

    }

    /**

     * 关闭当前线程使用的session

     */

    public static void closeSession() {

        Session session = (Session) sessionThread.get();

        if (session != null) {

            session.clear();

            session.close();

            sessionThread.set(null);

        }

    }

 

 /**
  * Create a HibernateTemplate for the given SessionFactory.
  * Only invoked if populating the DAO with a SessionFactory reference!
  * <p>Can be overridden in subclasses to provide a HibernateTemplate instance
  * with different configuration, or a custom HibernateTemplate subclass.
  * @param sessionFactory the Hibernate SessionFactory to create a HibernateTemplate for
  * @return the new HibernateTemplate instance
  * @see #setSessionFactory
  */
 protected HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) {
  return new HibernateTemplate(sessionFactory);
 }

 /**
  * Return the Hibernate SessionFactory used by this DAO.
  */
 public final SessionFactory getSessionFactory() {
  return (this.hibernateTemplate != null ? this.hibernateTemplate.getSessionFactory() : null);
 }

 /**
  * Set the HibernateTemplate for this DAO explicitly,
  * as an alternative to specifying a SessionFactory.
  * @see #setSessionFactory
  */
 public final void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
  this.hibernateTemplate = hibernateTemplate;
 }

 /**
  * Return the HibernateTemplate for this DAO,
  * pre-initialized with the SessionFactory or set explicitly.
  * <p><b>Note: The returned HibernateTemplate is a shared instance.</b>
  * You may introspect its configuration, but not modify the configuration
  * (other than from within an {@link #initDao} implementation).
  * Consider creating a custom HibernateTemplate instance via
  * <code>new HibernateTemplate(getSessionFactory())</code>, in which
  * case you're allowed to customize the settings on the resulting instance.
  */
 public final HibernateTemplate getHibernateTemplate() {
   return this.hibernateTemplate;
 }

 protected final void checkDaoConfig() {
  if (this.hibernateTemplate == null) {
   throw new IllegalArgumentException("'sessionFactory' or 'hibernateTemplate' is required");
  }
 }


 /**
  * Obtain a Hibernate Session, either from the current transaction or
  * a new one. The latter is only allowed if the
  * {@link org.springframework.orm.hibernate3.HibernateTemplate#setAllowCreate "allowCreate"}
  * setting of this bean's {@link #setHibernateTemplate HibernateTemplate} is "true".
  * <p><b>Note that this is not meant to be invoked from HibernateTemplate code
  * but rather just in plain Hibernate code.</b> Either rely on a thread-bound
  * Session or use it in combination with {@link #releaseSession}.
  * <p>In general, it is recommended to use HibernateTemplate, either with
  * the provided convenience operations or with a custom HibernateCallback
  * that provides you with a Session to work on. HibernateTemplate will care
  * for all resource management and for proper exception conversion.
  * @return the Hibernate Session
  * @throws DataAccessResourceFailureException if the Session couldn't be created
  * @throws IllegalStateException if no thread-bound Session found and allowCreate=false
  * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
  */
 

 /**
  * Obtain a Hibernate Session, either from the current transaction or
  * a new one. The latter is only allowed if "allowCreate" is true.
  * <p><b>Note that this is not meant to be invoked from HibernateTemplate code
  * but rather just in plain Hibernate code.</b> Either rely on a thread-bound
  * Session or use it in combination with {@link #releaseSession}.
  * <p>In general, it is recommended to use
  * {@link #getHibernateTemplate() HibernateTemplate}, either with
  * the provided convenience operations or with a custom
  * {@link org.springframework.orm.hibernate3.HibernateCallback} that
  * provides you with a Session to work on. HibernateTemplate will care
  * for all resource management and for proper exception conversion.
  * @param allowCreate if a non-transactional Session should be created when no
  * transactional Session can be found for the current thread
  * @return the Hibernate Session
  * @throws DataAccessResourceFailureException if the Session couldn't be created
  * @throws IllegalStateException if no thread-bound Session found and allowCreate=false
  * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
  */
 protected final Session getSession(boolean allowCreate)
     throws DataAccessResourceFailureException, IllegalStateException {

  return (!allowCreate ?
      SessionFactoryUtils.getSession(getSessionFactory(), false) :
    SessionFactoryUtils.getSession(
      getSessionFactory(),
      this.hibernateTemplate.getEntityInterceptor(),
      this.hibernateTemplate.getJdbcExceptionTranslator()));
 }

 /**
  * Convert the given HibernateException to an appropriate exception from the
  * <code>org.springframework.dao</code> hierarchy. Will automatically detect
  * wrapped SQLExceptions and convert them accordingly.
  * <p>Delegates to the
  * {@link org.springframework.orm.hibernate3.HibernateTemplate#convertHibernateAccessException}
  * method of this DAO's HibernateTemplate.
  * <p>Typically used in plain Hibernate code, in combination with
  * {@link #getSession} and {@link #releaseSession}.
  * @param ex HibernateException that occured
  * @return the corresponding DataAccessException instance
  * @see org.springframework.orm.hibernate3.SessionFactoryUtils#convertHibernateAccessException
  */
 protected final DataAccessException convertHibernateAccessException(HibernateException ex) {
  return this.hibernateTemplate.convertHibernateAccessException(ex);
 }

 /**
  * Close the given Hibernate Session, created via this DAO's SessionFactory,
  * if it isn't bound to the thread (i.e. isn't a transactional Session).
  * <p>Typically used in plain Hibernate code, in combination with
  * {@link #getSession} and {@link #convertHibernateAccessException}.
  * @param session the Session to close
  * @see org.springframework.orm.hibernate3.SessionFactoryUtils#releaseSession
  */
 protected final void releaseSession(Session session) {
  SessionFactoryUtils.releaseSession(session, getSessionFactory());
 }

}

 

然后在加个 Filter 用于请求结束时关掉对应的session

 

 

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

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

/**
 * @Description:
 * @Company:  * @author fanpj
 * @version 创建时间:9:02:29 AM, Sep 21, 2009
 * 类说明
 */
public class HibernateFilter implements Filter {

 public void destroy() {
  // TODO Auto-generated method stub

 }

 public void doFilter(ServletRequest req, ServletResponse res,
   FilterChain chain) throws IOException, ServletException {
  // TODO Auto-generated method stub
  try {

   chain.doFilter(req, res);

        } catch (IOException e) {

         e.printStackTrace();

        } catch (ServletException e) {

         e.printStackTrace();

        } finally {

         try {

          HibernateDaoSupport.commitTransaction();

         } catch (Exception e) {

          HibernateDaoSupport.rollbackTransaction();

         } finally {

          HibernateDaoSupport.closeSession();

         }
        }


 }

 public void init(FilterConfig arg0) throws ServletException {
  // TODO Auto-generated method stub

 }

}

 

搞定!!!

 

虽然最后的session全部都关闭了,但这个项目还是存在问题的,还好我们只是个演示版,在这提醒同仁们,一般情况下dao最好自己写个基类,不要继成开源项目自带的,这样项目的可扩展性能好些,也避免了继承类提供的多个数据操作入口。

 

原创粉丝点击