OpenSessionInViewFilter解决Web应用程序的问题 转自:Potain 的BLOG

OpenSessionInView

Created by potian. Last edited by admin 61 days ago. Viewed 181 times.
[edit] [attach]
Hibernate的Lazy初始化1:n关系时,你必须保证是在同一个Session内部使用这个关系集合,不然Hiernate将抛出例外。

另外,你不愿意你的DAO测试代码每次都打开关系Session,因此,我们一般会采用OpenSessionInView模式。

OpenSessionInViewFilter解决Web应用程序的问题

如果程序是在正常的Web程序中运行,那么Spring的OpenSessionInViewFilter能够解决问题,它:

protected void doFilterInternal(HttpServletRequest request, 
             HttpServletResponse response,
	     FilterChain filterChain) throws ServletException, IOException {
	SessionFactory sessionFactory = lookupSessionFactory();
	logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
	Session session = getSession(sessionFactory);
	TransactionSynchronizationManager.bindResource(sessionFactory, 
             new SessionHolder(session));
	try {
		filterChain.doFilter(request, response);
	}
	finally {
		TransactionSynchronizationManager.unbindResource(sessionFactory);
		logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
		closeSession(session, sessionFactory);
	}
}
可以看到,这个Filter在request开始之前,把sessionFactory绑定到TransactionSynchronizationManager,和这个SessionHolder相关。这个意味着所有request执行过程中将使用这个session。而在请求结束后,将和这个sessionFactory对应的session解绑,并且关闭Session。

为什么绑定以后,就可以防止每次不会新开一个Session呢?看看HibernateDaoSupport的情况:

publicfinal void setSessionFactory(SessionFactory sessionFactory) {
    this.hibernateTemplate = new HibernateTemplate(sessionFactory);
  }
 protectedfinal HibernateTemplate getHibernateTemplate() {
  return hibernateTemplate;
 }

我们的DAO将使用这个template进行操作:

publicabstract class BaseHibernateObjectDao
	extends HibernateDaoSupport
	implements BaseObjectDao {

protected BaseEntityObject getByClassId(finallong id) { BaseEntityObject obj = (BaseEntityObject) getHibernateTemplate() .execute(new HibernateCallback() {

publicObject doInHibernate(Session session) throws HibernateException { return session.get(getPersistentClass(), newLong(id)); }

}); return obj; }

public void save(BaseEntityObject entity) { getHibernateTemplate().saveOrUpdate(entity); }

public void remove(BaseEntityObject entity) { try {

getHibernateTemplate().delete(entity); } catch (Exception e) { thrownew FlexEnterpriseDataAccessException(e); } }

public void refresh(final BaseEntityObject entity) { getHibernateTemplate().execute(new HibernateCallback() {

publicObject doInHibernate(Session session) throws HibernateException { session.refresh(entity); returnnull; }

}); }

public void replicate(finalObject entity) { getHibernateTemplate().execute(new HibernateCallback() {

publicObject doInHibernate(Session session) throws HibernateException { session.replicate(entity, ReplicationMode.OVERWRITE); returnnull; }

}); }

而HibernateTemplate试图每次在execute之前去获得Session,执行完就力争关闭Session
publicObject execute(HibernateCallback action) throws DataAccessException {
	Session session = (!this.allowCreate ?
		SessionFactoryUtils.getSession(getSessionFactory(), 
                  false) :
		SessionFactoryUtils.getSession(getSessionFactory(),
                  getEntityInterceptor(),
                  getJdbcExceptionTranslator()));
	boolean existingTransaction =  
          TransactionSynchronizationManager.hasResource(getSessionFactory());
	if (!existingTransaction && getFlushMode() == FLUSH_NEVER) {
		session.setFlushMode(FlushMode.NEVER);
	}
	try {
		Object result = action.doInHibernate(session);
		flushIfNecessary(session, existingTransaction);
		return result;
	}
	catch (HibernateException ex) {
		throw convertHibernateAccessException(ex);
	}
	catch (SQLException ex) {
		throw convertJdbcAccessException(ex);
	}
	catch (RuntimeException ex) {
		// callback code threw application exception
		throw ex;
	}
	finally {
		SessionFactoryUtils.closeSessionIfNecessary(
                    session, getSessionFactory());
	}
}
而这个SessionFactoryUtils能否得到当前的session以及closeSessionIfNecessary是否真正关闭session,端取决于这个session是否用sessionHolder和这个sessionFactory在我们最开始提到的TransactionSynchronizationManager绑定。
publicstatic void closeSessionIfNecessary(Session session, 
    SessionFactory sessionFactory)   
    throws CleanupFailureDataAccessException {
	if (session == null || 
	   TransactionSynchronizationManager.hasResource(sessionFactory)) {
		return;
	}
	logger.debug("Closing Hibernate session");
	try {
		session.close();
	}
	catch (JDBCException ex) {
		// SQLException underneath
		thrownew CleanupFailureDataAccessException(
		"Cannot close Hibernate session", ex.getSQLException());
	}
	catch (HibernateException ex) {
		thrownew CleanupFailureDataAccessException(
		"Cannot close Hibernate session", ex);
	}
}

HibernateInterceptor和OpenSessionInViewInterceptor的问题

使用同样的方法,这两个Interceptor可以用来解决问题。但是关键的不同之处在于,它们的力度只能定义在DAO或业务方法上,而不是在我们的Test方法上,除非我们把它们应用到TestCase的方法上,但你不大可能为TestCase去定义一个接口,然后把Interceptor应用到这个接口的某些方法上。直接使用HibernateTransactionManager也是一样的。因此,如果我们有这样的测试:

Category parentCategory  = new Category ();
	parentCategory.setName("parent");
	dao.save(parentCategory);

Category childCategory = new Category(); childCategory.setName("child");

parentCategory.addChild(childCategory); dao.save(childCategory);

Category savedParent = dao.getCategory("parent"); Category savedChild = (Category ) savedParent.getChildren().get(0); assertEquals(savedChild, childCategory);

将意味着两件事情:
  • 每次DAO执行都会启动一个session和关闭一个session
  • 如果我们定义了一个lazy的关系,那么最后的Category savedChild = (Category ) savedParent.getChildren().get(0);将会让hibernate报错。

解决方案

一种方法是对TestCase应用Interceptor或者TransactionManager,但这个恐怕会造成很多麻烦。除非是使用增强方式的AOP.我前期采用这种方法(Aspectwerkz),在Eclipse里面也跑得含好。

另一种方法是在TestCase的setup和teardown里面实现和Filter完全一样的处理,其他的TestCase都从这个TestCase继承,这种方法是我目前所使用的。

Jolestar补充:openSessionInView的配置方法:

   <filter>
        <filter-name>opensession</filter-name>
        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
        <init-param>
            <param-name>singleSession</param-name>
            <param-value>false</param-value>
        </init-param>
    </filter>

posted on 2006-09-26 11:01 康文 阅读(547) 评论(0)  编辑  收藏 所属分类: java


只有注册用户登录后才能发表评论。


网站导航:
 
<2006年9月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(1)

随笔分类

随笔档案

文章档案

搜索

最新评论

阅读排行榜

评论排行榜