论坛首页 Java企业应用论坛

『提问』关闭session的老问题

浏览 20779 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2004-11-06  
软件环境:
我采用SLSB-->DAO-->Hibernate-->DB开发我的持久层,DAO中封装的方法是对PO的CURD操作,SLSB负责具体的业务逻辑。

我最初的期望是由DAO完成所有的底层操作,包括异常也全部以DAOException抛出,由于不想在SLSB中出现Hiberante代码,我把session的开、关,全部放在了DAO的方法操作中,并采用threadlocal方式管理,事务使用容器管理。

我的这种做法在单个DAO方法操作单个线程session时没有问题,但是当我在DAO方法中,希望调用其他的DAO操作时,由于每个操作都关闭本线程的session,会导致最初的DAO方法还没有执行完毕时,线程就被关闭了从而报错。

这个问题考虑良久,现在我处在两种解决方案的矛盾中:方法一:把session的关闭放到SLSB的业务方法中,在执行完DAO操作后再关闭,但是这样似乎DAO的封装就不再完整,记得robbin说过SLSB中不应出现Hibernate的代码;方法二:在DAO方法中不使用threadlocal,每个方法都开关自己的session,这样我已经可以正常实现我的所有操作了,但是看了以前的帖子,这似乎也不是个好的解决方案。

想了半天,没有一个好的结果,我把我的代码贴上来,可能我对threadLocal的理解不太正确,如果代码中的使用不对,请指正,谢谢!
配置文件:
这是一个目录类,在PO中我只定义了一个many-to-one的parentCategory属性,也就是说只有单向的关联。
...
public class CategoryDaoHibernateImpl implements CategoryDao {
...
//删除一个目录方法,执行时需要查找该目录的所有子目录并先把子目录删掉
  public boolean delCategory(Category category);  throws DaoException{
    Session session = null;
    boolean returnValue = false;
    try {
      session = HibernateUtil.currentSession();;
    }
    catch (InfrastructureException ex); {
      throw new DaoException("CategoryDao的delCategory方法发生InfrastructureException",ex);;
    }

  try {
    ArrayList childCate = (ArrayList);this.getChildCategory(category);;
    if(childCate!=null);{
      for(int i=0;i<childCate.size();;i++);{
        this.delCategory((Category);childCate.get(i););;
      }
    }
    session.delete(category);;
    session.flush();;
    returnValue = true;
  }
  catch (HibernateException ex); {
    throw new DaoException("CategoryDao的delCategory方法发生HibernateException",ex);;
  }
  finally{
    try {
    HibernateUtil.closeSession();;
  }
  catch (InfrastructureException ex); {
          throw new DaoException("CategoryDao的delCategory方法发生InfrastructureException",ex);;
  }


//取得子目录方法,得到该目录下的所有子目录
  public Collection getChildCategory(Category parentCategory);throws DaoException{
    Session session = null;
    ArrayList category = null;
    try {
      session = HibernateUtil.currentSession();;
    }
    catch (InfrastructureException ex); {
      throw new DaoException("CategoryDao的getChildCategory方法发生InfrastructureException",ex);;
    }

    try {
      Query q = session.createQuery("select from Category as category where category.parentCategory=:parentCategory");;
      q.setParameter("parentCategory",parentCategory);;
      category = (ArrayList);q.list();;
      if(category.size();==0);{
        System.out.println("没有找到与parentCategory: id ="+parentCategory.getId();+"对应的区域对象");;
        return null;
      }
      else{System.out.println("category.size= "+category.size(););;}
      session.flush();;
    }
    catch (HibernateException ex); {
      throw new DaoException("CategoryDao的getChildCategory方法发生HibernateException",ex);;
    }
    finally{
      try {
        HibernateUtil.closeSession();;
        System.out.println("改改后的getChildCategory的finally");;
      }
      catch (InfrastructureException ex); {
              throw new DaoException("CategoryDao的getChildCategory方法发生InfrastructureException",ex);;
      }

    }
    return category;
 }



EJB中的调用就是一句:
    
returnValue = categoryDao.delCategory(category);;  

错误提示信息:

11:21:14,359 ERROR SessionImpl:2343 - Could not synchronize database state with session

net.sf.hibernate.HibernateException: Session is closed


你的分析:
出错信息很明显,就是我在查找子目录的方法中关掉了删除目录方法中的session,只是这个问题到底应该怎么样解决才是比较完美的呢,谢谢
   发表时间:2004-11-08  
我的做法是在所有的方法里都不关闭,
最后在filter里统一关闭
0 请登录后投票
   发表时间:2004-11-08  
yangstarfly 写道
我的做法是在所有的方法里都不关闭,
最后在filter里统一关闭

你指的是servlet的filter 吗?我的DAO是提供给EJB使用的,我希望能在DAO这一层本身完全控制Hibernate,退一步在EJB中统一关闭也可以吧,在EJB中是否也可以实现对针对具体方法的Filter?

Filter模式能不能实现在一个DAO或者EJB中?对Filter不太了解的说
0 请登录后投票
   发表时间:2004-11-08  
在看了你的问题后,和你不同的地方主要有两点。
1。ThreadLocal
在使用ThreadLocal上,我们的做法也是和你的一样,但是名字不叫HibernateUtil, 而是PersistenceUtil.
这样的话在业务逻辑里调用PersistenceUtil也没有违反"在业务逻辑中不出现Hiternate的code"定律

2。Transaction
管理Transaction是在SLSB或者业务对象里面管理。以下的事件是在业务逻辑实现的,DAO里只是从ThreadLocal里去取到一个session.
begin transaction
commit
rollback
session close

public CompanyModel addCompany(CompanyModel model);{
  try{
    PersistenceUtil.beginTransaction();;
    CompanyModel result = (CompanyModel);companyDAO.create(model);;
    PersistenceUtil.commitTransaction();;
    return result;
  }catch(PersistenceException pe);{
    PersistenceUtil.rollbackTransaction();;
    throw new BusinessException(pe);;
  }finally{
    PersistenceUtil.closeSession();;
  }
}
0 请登录后投票
   发表时间:2004-11-08  
上一篇文章里的code是SLSB里的代码。

以下是DAO里的代码
public Object create(Object model); throws DAOException{
  try{
    Session s = HibernateUtil.currentSession();;
    s.save( model );;
  }catch(HibernateException he);{
    throw new DAOException(he);;
  }
  return model;
}
0 请登录后投票
   发表时间:2004-11-08  
谢谢你的回复,你们这样的实现应该是比较清晰合理的,很有参考价值,谢谢:)
不知能否贴出PersistenceUtil的代码,想看看你们对Transaction的封装,因为我对事务的处理仅仅是在EJB部署描述里申明为“Required”,然后在catch到异常的时候调用 sessionContext.setRollbackOnly(),这样做不知道有没有隐患
0 请登录后投票
   发表时间:2004-11-09  
以下代码供参考

PersistenceUtil.java
public class PersistenceUtil {
  private static Log log = LogFactory.getLog(PersistenceUtil.class);;

  public static Session currentSession(); throws PersistenceException {
    try {
      return HibernateUtil.currentSession();;
    } catch (HibernateException he); {
      throw new PersistenceException(he);;
    }
  }

  public static void closeSession(); throws PersistenceException {
    try {
      HibernateUtil.closeSession();;
    } catch (HibernateException he); {
      throw new PersistenceException(he);;
    }
  }

  public static void beginTransaction(); throws PersistenceException {
    try {
      HibernateUtil.beginTransaction();;
    } catch (HibernateException he); {
      throw new PersistenceException(he);;
    }
  }

  public static void commitTransaction(); throws PersistenceException {
    try {
      HibernateUtil.commitTransaction();;
    } catch (HibernateException he); {
      throw new PersistenceException(he);;
    }
  }

  public static void rollbackTransaction(); throws PersistenceException {
    try {
      HibernateUtil.rollbackTransaction();;
    } catch (HibernateException he); {
      throw new PersistenceException(he);;
    }
  }
}



HibernateUtil.java
public class HibernateUtil {

  private static Log log = LogFactory.getLog(HibernateUtil.class);;

  private static SessionFactory sessionFactory;
  private static final ThreadLocal threadSession = new ThreadLocal();;
  private static final ThreadLocal threadTransaction = new ThreadLocal();;

  public static SessionFactory getSessionFactory();{
    if(sessionFactory == null);{
      try {
        // Create the SessionFactory
        sessionFactory = new Configuration();.configure();.buildSessionFactory();;
      } catch (HibernateException ex); {
        ex.printStackTrace();;
        throw new RuntimeException("Configuration problem: " + ex.getMessage();, ex);;
      }
    }
    return sessionFactory;
  }

  public static Session currentSession(); throws HibernateException {
    Session s = (Session); threadSession.get();;
    // Open a new Session, if this Thread has none yet
    if (s == null); {
      s = getSessionFactory();.openSession();;
      log.debug("###Opening new Session for this thread:" + s);;
      threadSession.set(s);;
    }else{
      log.debug("###Session was existed:" + s);;
    }
    return s;
  }

  public static void closeSession(); throws HibernateException {
    Session s = (Session); threadSession.get();;
    threadSession.set(null);;
    if (s != null);{
      log.debug("###Closing Session of this thread. " + s);;
      s.close();;
    }
  }

  public static void beginTransaction();
    throws HibernateException {
    Transaction tx = (Transaction); threadTransaction.get();;
    try {
      if (tx == null); {
        tx = currentSession();.beginTransaction();;
        log.debug("###Starting new database transaction in this thread:" + tx);;
        threadTransaction.set(tx);;
      }else{
        log.debug("###Tx was existed:" + tx);;
      }
    } catch (HibernateException ex); {
      throw ex;
    }
  }

  public static void commitTransaction();
    throws HibernateException {
    Transaction tx = (Transaction); threadTransaction.get();;
    try {
      if ( tx != null && !tx.wasCommitted();
              && !tx.wasRolledBack(); ); {
        log.debug("###Committing database transaction of this thread.");;
        tx.commit();;
      }
      threadTransaction.set(null);;
    } catch (HibernateException ex); {
      rollbackTransaction();;
      throw ex;
    }
  }

  public static void rollbackTransaction();
    throws HibernateException {
    Transaction tx = (Transaction); threadTransaction.get();;
    try {
      threadTransaction.set(null);;
      if ( tx != null && !tx.wasCommitted(); && !tx.wasRolledBack(); ); {
        log.debug("###Tyring to rollback database transaction of this thread.");;
        tx.rollback();;
      }
    } catch (HibernateException ex); {
      throw ex;
    } finally {
      closeSession();;
    }
  }

}
0 请登录后投票
   发表时间:2004-11-09  
多谢bean,这代码写的比我漂亮多了,学习中:)
0 请登录后投票
   发表时间:2004-11-09  
又翻出了很早以前看过的帖子:
http://www.hibernate.org.cn/56.html
也是robbin的大作,他一再强调用容器管理事务是不不需要写Transaction代码的,只要在部署描述里面配置,我现在用的就是这种方法,不过bean你们用的方法好像是使用了JTA,请问你们用的应该是BMT是吗?
0 请登录后投票
   发表时间:2004-11-09  
我们在使用Hibernate的这个项目里,没有用EJB.
我的那段代码addCompany(CompanyModel model)。。。是在业务逻辑类里面实现的。
前面的有一篇文章里我写的是说在SLSB里写的。是我说错了,对不起!
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics