转载:OpenSessionInViewFilter

转自:Potain 的BLOG

OpenSessionInViewFilter

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的情况:

public final void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
protected final HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}

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

public abstract class BaseHibernateObjectDao
extends HibernateDaoSupport
implements BaseObjectDao {


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

public Object doInHibernate(Session session)
throws HibernateException {
return session.get(getPersistentClass(),
new Long(id));
}

});
return obj;
}

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

public void remove(BaseEntityObject entity) {
try {

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

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

public Object doInHibernate(Session session)
throws HibernateException {
session.refresh(entity);
return null;
}

});
}

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

public Object doInHibernate(Session session)
throws HibernateException {
session.replicate(entity,
ReplicationMode.OVERWRITE);
return null;
}

});
}

而HibernateTemplate试图每次在execute之前去获得Session,执行完就力争关闭Session
public Object 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绑定。
public static 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
throw new CleanupFailureDataAccessException(
"Cannot close Hibernate session", ex.getSQLException());
}
catch (HibernateException ex) {
throw new 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>


 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值