hibernate的Dao作为单例时需要注意的问题

      今天在调试hibernate的Dao层单例的时候,在一个查询用户的概览页面,第一次刷新页面的时候,用户列表能正确展示。但是当用F5刷新页面的时候,发现用户列表不能正确的展示出来,报出一个hibernate异常,Session is Closed!由于我采用的是one-session-per-request模式,按道理session不应该会被关闭,除非该session会被commit或者close掉。重新读了一遍代码,没有发现有这样的代码,在无法确定问题的时候,我只能把当前报错的位置的session的hashcode打印出来,结果报错位置的session的hashCode永远与第一次进入页面的session的hashCode一致,相当于永远使用了一个UserMgr(Dao层)对象。仔细看下UserMgr对象,果不其然,我是用的是单例模式,而单例模式只set了一次session,所以以后每次从UserMgr中取的session对象都是被关闭掉的session对象。修改一下代码,把每次使用的session都从当前线程中获取,错误解决。

    主要注意事项:

     (1)hibernate.cfg.xml中增加以下配置,设置session的在当前线程中有效。

          <!-- 设置 上下文运行条件,value可以为:jta/thread/managed  --> 
        <property name="hibernate.current_session_context_class">thread</property>

     (2)通过HibernateSessionRequestFilter的一个filter来保证“one-session-per-request模式”

     (3)所有Mgr对象(Dao)都要继承自BaseHibernateDAO类,这个类中提供了一些cud的一些基本操作

 

1.HibernateSessionRequestFilter的代码如下:

 

/**
 * 提供所有的jsp访问的时候,执行hibernate的 Transaction的开始和提交
 *
 * @author rey
 *
 */
public class HibernateSessionRequestFilter implements Filter {

 /**
  * 记录日志
  */
 private static Logger logger = Logger
   .getLogger(HibernateSessionRequestFilter.class);
 private SessionFactory sf;

 /*
  * (non-Javadoc)
  *
  * @see javax.servlet.Filter#destroy()
  */
 @Override
 public void destroy() {
  sf.close();
 }

 /*
  * (non-Javadoc)
  *
  * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
  * javax.servlet.ServletResponse, javax.servlet.FilterChain)
  */
 @Override
 public void doFilter(ServletRequest _request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {
  try {

   sf.getCurrentSession().beginTransaction();

   if (logger.isDebugEnabled()) {
    logger
      .debug("Starting a database transaction,session.hashCode::"
        + sf.getCurrentSession().hashCode());
   }

   // Call the next filter (continue request processing)
   chain.doFilter(_request, response);

   // Commit and cleanup
   if (logger.isDebugEnabled()) {
    logger
      .debug("Committing the database transaction,session.hashCode::"
        + sf.getCurrentSession().hashCode());
   }
   sf.getCurrentSession().getTransaction().commit();
  } catch (StaleObjectStateException staleEx) {
   logger
     .error("This interceptor does not implement optimistic concurrency control!");
   logger
     .error("Your application will not work until you add compensation actions!");
   // Rollback, close everything, possibly compensate for any permanent
   // changes
   // during the conversation, and finally restart business
   // conversation. Maybe
   // give the user of the application a chance to merge some of his
   // work with
   // fresh data... what you do here depends on your applications
   // design.
   throw staleEx;
  } catch (Throwable ex) {
   // Rollback only
   ex.printStackTrace();
   try {
    if (sf.getCurrentSession().getTransaction().isActive()) {
     logger
       .debug("Trying to rollback database transaction after exception");
     sf.getCurrentSession().getTransaction().rollback();
    }
   } catch (Throwable rbEx) {
    logger.error("Could not rollback transaction after exception!",
      rbEx);
   }

   // Let others handle it... maybe another interceptor for exceptions?
   throw new ServletException(ex);
  }

 }

 /*
  * (non-Javadoc)
  *
  * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
  */
 @Override
 public void init(FilterConfig arg0) throws ServletException {
  logger.debug("Initializing filter...");
  logger
    .debug("Obtaining SessionFactory from static HibernateUtil singleton");
  sf = HibernateUtil.getSessionFactory();

 }

}

2.BaseHibernateDAO代码如下:

 /**
 * 所有Mgr类都要继承的抽象类
 *
 * @author rey
 *
 */
public class BaseHibernateDAO<T, ID extends Serializable> implements
  IBaseHibernateDAO<T, ID> {
 private Class<T> persistentClass;

 public BaseHibernateDAO() {
  // 根据泛型中传递的参数T来初始化当前需要持久化的对象,取第一个参数,跟泛型的参数位置有关系
  this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
    .getGenericSuperclass()).getActualTypeArguments()[0];
 }

 /**
  * 获取当前mgr中的session,由于Mgr大部分都被设计成单例,所以每次都需要从当前thread中获取
  *
  * @return
  */
 protected Session getSession() {
  // 由于是单例模式,所以每次session都要重新获取,从当前线程中获取
  return HibernateUtil.getCurrentSession();

 }

 public Class<T> getPersistentClass() {
  return persistentClass;
 }

 @SuppressWarnings("unchecked")
 public T findById(ID id, boolean lock) {
  T entity;
  if (lock)
   entity = (T) getSession().load(getPersistentClass(), id,
     LockMode.UPGRADE);
  else
   entity = (T) getSession().load(getPersistentClass(), id);

  return entity;
 }

 @SuppressWarnings("unchecked")
 public List<T> findAll() {
  return findByCriteria();
 }

 /**
  * 根据实例的条件来查询集合<BR>
  * Example(继承了Criteria对象)过滤条件来查询指定对象集合
  *
  * @param exampleInstance
  *            需要查询的类的对象实例,不是xx.class
  * @param excludeProperty
  *            不提取的字段名称
  * @return
  */
 @SuppressWarnings("unchecked")
 public List<T> findByExample(T exampleInstance, String[] excludeProperty) {
  Criteria crit = getSession().createCriteria(getPersistentClass());
  Example example = Example.create(exampleInstance);
  for (String exclude : excludeProperty) {
   example.excludeProperty(exclude);
  }
  crit.add(example);
  return crit.list();
 }

 @SuppressWarnings("unchecked")
 public T makePersistent(T entity) {
  getSession().saveOrUpdate(entity);
  return entity;
 }

 public void makeTransient(T entity) {
  getSession().delete(entity);
 }

 public void flush() {
  getSession().flush();
 }

 public void clear() {
  getSession().clear();
 }

 /**
  * 根据过滤条件,查询满足条件的集合 <BR>
  * Criterion:为过滤条件<BR>
  * Criteria:过滤条件的执行者 <BR>
  * Use this inside subclasses as a convenience method.
  */
 @SuppressWarnings("unchecked")
 protected List<T> findByCriteria(Criterion... criterion) {
  Criteria crit = getSession().createCriteria(getPersistentClass());
  for (Criterion c : criterion) {
   crit.add(c);
  }
  return crit.list();
 }

 /**
  * 查询一个对象的个数,按照这个字段和条件来统计这个字段
  *
  * @param _sCountSelect
  *            需要统计的字段,一般为一个持久化对象的主键id
  * @param criterion
  *            需要按照某种条件来过滤和统计
  * @return
  * @throws MyException
  */
 public int getTotalSize(String _sCountSelect, Criterion... criterion) throws MyException {
  Criteria crit = getSession().createCriteria(getPersistentClass());
  crit.setProjection(Projections.count(_sCountSelect));
  for (Criterion c : criterion) {
   crit.add(c);
  }
  Object oTotalNumber = crit.uniqueResult();
  if (oTotalNumber == null) {
   throw new MyException("查询总条数时发生错误[CountSelect::" + _sCountSelect
     + "]");
  }

  return Integer.parseInt(oTotalNumber.toString());
 }

 /**
  *
  * @param _nPageIndex
  *            查询页面索引,第一页从1开始
  * @param _nPageSize
  *            页面显示的记录数
  * @param _nItemCoun
  *            记录总数
  * @param _criterion
  *            查询条件
  * @return
  */
 protected List<T> getPageItems(int _nPageIndex, int _nPageSize,
   int _nItemCoun) {

  Criteria crit = getSession().createCriteria(getPersistentClass());

  // 构造分页参数
  MyPage CurrPage = new MyPage(_nPageSize);
  CurrPage.setItemCount(_nItemCoun);
  CurrPage.setCurrPageIndex(_nPageIndex);

  // 这里的起始值是0,MyPage的起始值为1
  crit.setFirstResult(CurrPage.getFirstItemIndex() - 1);
  crit.setMaxResults(CurrPage.getCurrPageSize()); // 获取当前页大小
  return crit.list();
 }

 @Override
 public List<T> findByExample(T exampleInstance) {
  // TODO Auto-generated method stub
  return null;
 }

}

 

 3.继承自BaseHibernateDAO类的一个Mgr对象,UserMgr代码:

 /**
 * 用户对象业务逻辑管理类,只负责业务逻辑的处理
 *
 * @author rey
 *
 */
public class UserMgr extends BaseHibernateDAO<User, Long> {

 /**
  * 单例对象
  */
 private static UserMgr m_SingleMgr = null;

 /**
  * 单例方法
  *
  * @return
  */
 public static UserMgr getInstance() {
  if (m_SingleMgr == null) {
   m_SingleMgr = new UserMgr();
  }
  return m_SingleMgr;
 }

 // 构造方法
 public UserMgr() {
  super();
 }

 /**
  * 根据用户id获取用户对象
  *
  * @param _lUserId
  *            用户id
  * @return
  * @throws MyException
  */
 public User findById(long _lUserId) {
  return (User) super.findById(_lUserId, false);
 }

 // 提供外接的查询函数================
 /**
  * 保存用户到数据库中,包括新增和修改操作
  *
  * @param _saveUser
  *            要保存的用户
  * @return
  * @throws MyException
  */
 public User saveOrUpdate(User _saveUser) throws MyException {
  if (_saveUser == null)
   throw new MyException("保存的用户对象为null!");

  return (User) this.makePersistent(_saveUser);
 }

 /**
  * 删除一个用户
  *
  * @param _deleteUser
  *            要删除的用户对象
  * @throws MyException
  */
 public void delete(User _deleteUser) throws MyException {
  if (_deleteUser == null)
   throw new MyException("要删除的用户对象为null!");
  this.makeTransient(_deleteUser);
 }

 /**
  * 删除指定ids的用户信息,返回删除的用户个数
  *
  * @param _sDeleteUserIds
  * @throws MyException
  */
 public int delete(String _sDeleteUserIds) throws MyException {
  // 1.如果用sql删除,则可能缓存会有问题
  // 2.如果一条一条删除则会有性能问题,综合考虑采用批处理来删除
  if (_sDeleteUserIds == null) {
   return 0;
  }
  StringBuilder sbHQL = new StringBuilder(
    "delete from User where UserId in(?");
  String[] aUserIds = _sDeleteUserIds.split(",");
  for (int i = 1; i < aUserIds.length; i++) {
   sbHQL.append(",?");
  }
  sbHQL.append(")");

  // 构造查询
  Query deleteQuery = getSession().createQuery(sbHQL.toString());

  // 添加参数
  for (int i = 0; i < aUserIds.length; i++) {
   deleteQuery.setString(i, aUserIds[i]);
  }
  return deleteQuery.executeUpdate();
 }

 /**
  *
  * @param _nPageIndex
  *            查询页面索引,第一页从1开始
  * @param _nPageSize
  *            页面记录条数
  *
  * @param _nItemCount
  *            页面记录总条数
  * @return
  */
 public List<User> getPageUsers(int _nPageIndex, int _nPageSize,
   int _nItemCount) {

  return this.getPageItems(_nPageIndex, _nPageSize, _nItemCount);
 }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

O溺水的鱼0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值