首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Spring解决Hibernate session 封锁

2012-09-01 
Spring解决Hibernate session 关闭在你得 web.xml 文件里面加上下面的配置信息: filterfilter-nameOpe

Spring解决Hibernate session 关闭

在你得 web.xml 文件里面加上下面的配置信息:

 <filter>   <filter-name>OpenSessionInViewFilter</filter-name>    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>    </filter> <filter-mapping>   <filter-name>OpenSessionInViewFilter</filter-name>    <url-pattern>/*</url-pattern>  </filter-mapping>

?

深入分析OpenSessionInViewFilter的内幕最的在项目中发现Spring中对OpenSessionInViewFilter的作用,感觉很有意思,不由得自己的好奇心,开始研究起它的作用起来,总结如下:
Spring中对OpenSessionInViewFilter的描述如下:
?????它是一个Servlet2.3过滤器,用来把一个Hibernate?Session和一次完整的请求过程对应的线程相绑定。目的是为了实现"Open?Session?in?View"的模式。例如:?它允许在事务提交之后延迟加载显示所需要的对象。
下面从处理请求的入口读起,下面所指的session均为hibernate?session不再特别说明,本文观点纯属个人观点如有错误请批评指正不胜感激.

?

OpenSessionInViewFilter?的父类OncePerRequestFilter(抽象类)的方法,是过滤器的入口,是处理请求的第一个方法.?

public?final?void?doFilter?
(?ServletRequest?request,?ServletResponse?response,?FilterChain?filterChain)?
????????throws?ServletException,?IOException?{?
????//首选判断进行过滤的是否是http请求?
????if?(!(request?instanceof?HttpServletRequest)?||?!(response?instanceof?HttpServletResponse))?{?
????????throw?new?ServletException("OncePerRequestFilter?just?supports?HTTP?requests");?
????}?
????//如果是http请求的话进行强转?
????HttpServletRequest?httpRequest?=?(HttpServletRequest)?request;?
????HttpServletResponse?httpResponse?=?(HttpServletResponse)?response;?
????//alreadyFilteredAttributeName?是一个标识,用于判断是否需要进行OpenSessionInViewFilter?
????String?alreadyFilteredAttributeName?=?getAlreadyFilteredAttributeName();?
????if?(request.getAttribute(alreadyFilteredAttributeName)?!=?null?||?shouldNotFilter(httpRequest))?{?
????????//?Proceed?without?invoking?this?filter...?
????????filterChain.doFilter(request,?response);?
????}?
????else?{?
????????//?Do?invoke?this?filter...?
????????request.setAttribute(alreadyFilteredAttributeName,?Boolean.TRUE);?
????????//下面这个方法是abstract方法由OpenSessionInViewFilter?实现,是OpenSessionInViewFilter?的核心方法?
????????doFilterInternal(httpRequest,?httpResponse,?filterChain);?
????}?
}?
好下面让我们进入doFilterInternal一探究竟




protected?void?doFilterInternal(HttpServletRequest?request,?
????????HttpServletResponse?response,?FilterChain?filterChain)?
????????throws?ServletException,?IOException?{?
????/**?
?????*?从spring的上下文中取得SessionFactory对象?
?????*?WebApplicationContext?wac?=?WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());?
?????*?return?(SessionFactory)?wac.getBean(getSessionFactoryBeanName(),SessionFactory.class);?
?????*?getSessionFactoryBeanName()方法默认返回"sessionFactory"字符串,在spring配置文件中可要注意了,别写错了.?
?????*/?
????SessionFactory?sessionFactory?=?lookupSessionFactory(request);?
????boolean?participate?=?false;//?标识过滤器结束时是否进行关闭session等后续处理?
????if?(isSingleSession())?{//单session模式?
????????//判断能否在当前线程中取得sessionFactory对象对应的session?
????????if?(TransactionSynchronizationManager.hasResource(sessionFactory))?{?
????????????//当能够找到session的时候证明会有相关类处理session的收尾工作,这个过滤器不能进行关闭session操作,否则会出现重复关闭的情况.?
????????????participate?=?true;//但我并没有想出正常使用的情况下什么时候会出现这种情况.?
????????}?else?{?
????????????Session?session?=?getSession(sessionFactory);//当前线程取不到session的时候通过sessionFactory创建,下面还会详细介绍此方法?
????????????//将session绑定到当前的线程中,SessionHolder是session的一层封装,里面有个存放session的map,而且是线程同步的Collections.synchronizedMap(new?HashMap(1));?
????????????//但是单session模式下一个SessionHolder对应一个session,核心方法是getValidatedSession?取得一个open状态下的session,并且取出后从map中移出.?
????????????TransactionSynchronizationManager.bindResource(sessionFactory,?
;????????????????new?SessionHolder(session));?

????????}?
????}?else?{//这段是非单session模式的处理情况,没有研究.但粗略看上去,大概思想是一样的?
????????if?(SessionFactoryUtils.isDeferredCloseActive(sessionFactory))?{?
????????????participate?=?true;?
????????}?else?{?
????????????SessionFactoryUtils.initDeferredClose(sessionFactory);?
????????}?
????}?
????try?{?
????????//将session绑定到了当前线程后,就该处理请求了?
????????filterChain.doFilter(request,?response);?
????}finally?{?
????????if?(!participate)?{?
????????????if?(isSingleSession())?{?
????????????????//当请求结束时,对于单session模式,这时候要取消session的绑定,因为web容器(Tomcat等)的线程是采用线程池机制的,线程使用过后并不会销毁.?
????????????????SessionHolder?sessionHolder?=?(SessionHolder)?TransactionSynchronizationManager?
????????????????????????.unbindResource(sessionFactory);?
????????????????//取消绑定只是取消session对象和线程对象之间的引用,还没有关闭session,不关闭session相对于不关闭数据库连接,所以这里要关闭session?
????????????????closeSession(sessionHolder.getSession(),?sessionFactory);?
????????????}?else?{?
????????????????//非单session模式,没有研究.?
????????????????SessionFactoryUtils.processDeferredClose(sessionFactory);?
????????????}?
????????}?
????}?
}?
下面详细介绍TransactionSynchronizationManager的几个关键的方法



public?abstract?class?TransactionSynchronizationManager?{?
//线程局部变量,为每一个使用该变量的线程都提供一个变量值的副本,每一个线程都可以独立地改变自己的副本,而不会与其它线程的副本冲突,用于存放session?
private?static?final?ThreadLocal?resources?=?new?ThreadLocal();?
public?static?boolean?hasResource(Object?key)?{//判断当前线程是否已经绑定了session,key是sessionFactory对象,一个sessionFactory可以绑定一个session?
????Assert.notNull(key,?"Key?must?not?be?null");//spring的Assert类不错,大家可以看看很简单?
????Map?map?=?(Map)?resources.get();?
????return?(map?!=?null?&&?map.containsKey(key));?
}?
public?static?void?bindResource(Object?key,?Object?value)?throws?IllegalStateException?{//绑定session到当前线程?
????Assert.notNull(key,?"Key?must?not?be?null");?
????Assert.notNull(value,?"Value?must?not?be?null");?
????Map?map?=?(Map)?resources.get();//ThreadLocal对象只可以存放一个对象,所以使用map来扩展?
????if?(map?==?null)?{?
????????map?=?new?HashMap();?
????????resources.set(map);?
????}?
????if?(map.containsKey(key))?{?
????????throw?new?IllegalStateException("Already?value?["?+?map.get(key)?+?"]?for?key?["?+?key?+?
????????????????"]?bound?to?thread?["?+?Thread.currentThread().getName()?+?"]");?
????}?
????map.put(key,?value);?
}?
static?Object?unbindResource(Object?key)?throws?IllegalStateException?{//取消当前线程对session的绑定?
????Assert.notNull(key,?"Key?must?not?be?null");?
????Map?map?=?(Map)?resources.get();?
????if?(map?==?null?||?!map.containsKey(key))?{?
????????throw?new?IllegalStateException(?
????????????????"No?value?for?key?["?+?key?+?"]?bound?to?thread?["?+?Thread.currentThread().getName()?+?"]");?
????}?
????Object?value?=?map.remove(key);?
????if?(map.isEmpty())?{?
????????resources.set(null);?
????}?
????return?value;?
}?
另外一个非常关键的方法是OpenSessionInViewFilter的getSession方法,我们看这个方法的关键并不是如何取得session,而且注意这里设置了FlushMode?
protected?Session?getSession(SessionFactory?sessionFactory)?
????????throws?DataAccessResourceFailureException?{?
????Session?session?=?SessionFactoryUtils.getSession(sessionFactory,?true);?
????FlushMode?flushMode?=?getFlushMode();//默认情况下是FlushMode.NEVER?
????if?(flushMode?!=?null)?{?
????????session.setFlushMode(flushMode);?
????}?
????return?session;?
}?
读到这个地方,大家对ThreadLocal?感兴趣的话,可以看下我以前写的一篇文章http://blog.csdn.net/sunyujia/archive/2008/06/15/2549564.aspx
FlushMode.NEVER:
调用Session的查询方法时,不清理缓存
调用Session.commit()时,不清理缓存
调用Session.flush()时,清理缓存
不过FlushMode.NEVER已经不再建议使用了
官方描述如下
Deprecated.?use?MANUAL?instead.?使用FlushMode.MANUAL来代替
The?Session?is?never?flushed?unless?Session.flush()?is?explicitly?called?by?the?application.?This?mode?is?very?efficient?for?read?only?transactions.?
直到调用Session.flush()才会将变化反应到数据库,在只读的情况下是效率非常高的?
详见http://www.hibernate.org/hib_docs/v3/api/org/hibernate/FlushMode.html
我们来细看SessionFactoryUtils.getSession(sessionFactory,?true);



public?static?Session?getSession(SessionFactory?sessionFactory,?boolean?allowCreate)?throws?DataAccessResourceFailureException,?IllegalStateException?{?
????try?{?
????????return?doGetSession(sessionFactory,?null,?null,?allowCreate);?
????}?
????catch?(HibernateException?ex)?{?
????????throw?new?DataAccessResourceFailureException("Could?not?open?Hibernate?Session",?ex);?
????}?
}?
从上面可以看出doGetSession才是真正的核心方法,这里非常重要的是HibernateTemplate也是调用此方法



protected?Session?getSession()?{?
????if?(isAlwaysUseNewSession())?{?
????????return?SessionFactoryUtils.getNewSession(getSessionFactory(),?getEntityInterceptor());?
????}else?if?(!isAllowCreate())?{?
????????return?SessionFactoryUtils.getSession(getSessionFactory(),?false);?
????}else?{?
????????return?SessionFactoryUtils.getSession(?
????????????????getSessionFactory(),?getEntityInterceptor(),?getJdbcExceptionTranslator());?
????}?
}?
无论如何调用最后都是落实到SessionFactoryUtils.doGetSession这个方法上面,无论这个方法最后是否返回了null实际上在方法中都取得了session,
这是整个流程中最复杂的一个方法,部分代码我理解的也很不好,大家一起研究研究.



private?static?Session?doGetSession(?
????????SessionFactory?sessionFactory,?Interceptor?entityInterceptor,?
????????SQLExceptionTranslator?jdbcExceptionTranslator,?boolean?allowCreate)?
????????throws?HibernateException,?IllegalStateException?{?
????Assert.notNull(sessionFactory,?"No?SessionFactory?specified");?
????//取当前线程绑定?
的session?
????SessionHolder?sessionHolder?=?(SessionHolder)?TransactionSynchronizationManager.getResource(sessionFactory);?
????//sessionHolder就可以看成是当前线程绑定的session了?
????if?(sessionHolder?!=?null?&&?!sessionHolder.isEmpty())?{?
????????Session?session?=?null;?
????????if?(TransactionSynchronizationManager.isSynchronizationActive()?&?
????????????????sessionHolder.doesNotHoldNonDefaultSession())?{//判断spring的事务管理是否是激活的,同时SessionHolder对象中有且仅有一个session?
????????????//?Spring?transaction?management?is?active?->?
????????????//?register?pre-bound?Session?with?it?for?transactional?flushing.?
????????????session?=?sessionHolder.getValidatedSession();?
????????????//在开事务的时候HibernateTransactionManager类中的doBegin方法会将isSynchronizedWithTransaction设置为true,暂时不知道什么情况下会进入如下代码块?
????????????if?(session?!=?null?&&?!sessionHolder.isSynchronizedWithTransaction())?{?
????????????????TransactionSynchronizationManager.registerSynchronization(?
????????????????????????new?SpringSessionSynchronization(sessionHolder,?sessionFactory,?jdbcExceptionTranslator,?false));?
????????????????sessionHolder.setSynchronizedWithTransaction(true);?
????????????????//?Switch?to?FlushMode.AUTO,?as?we?have?to?assume?a?thread-bound?Session?
????????????????//?with?FlushMode.NEVER,?which?needs?to?allow?flushing?within?the?transaction.?
????????????????FlushMode?flushMode?=?session.getFlushMode();?
????????????????if?(flushMode.lessThan(FlushMode.COMMIT)?&?
????????????????????????!TransactionSynchronizationManager.isCurrentTransactionReadOnly())?{?
????????????????????session.setFlushMode(FlushMode.AUTO);?
????????????????????sessionHolder.setPreviousFlushMode(flushMode);?
????????????????}?
????????????}?
????????}?
????????else?{?
????????????//?No?Spring?transaction?management?active?->?try?JTA?transaction?synchronization.?
????????????//在没用事务的情况下下面的方法中只调用了sessionHolder.getValidatedSession();?
????????????session?=?getJtaSynchronizedSession(sessionHolder,?sessionFactory,?jdbcExceptionTranslator);?
????????}?
????????if?(session?!=?null)?{?
????????????return?session;//在使用了OpenSessionInViewFilter的情况下,HibernateTemplate执行此方法会在这里return?
????????}?
????}?
????//如果当前线程没有绑定session那么无论如何都是要创建session的,但是否会return还要取决于allowCreate等条件,在后面会看到?
????Session?session?=?(entityInterceptor?!=?null???
????????????sessionFactory.openSession(entityInterceptor)?:?sessionFactory.openSession());?

????//?Use?same?Session?for?further?Hibernate?actions?within?the?transaction.?
????//?Thread?object?will?get?removed?by?synchronization?at?transaction?completion.?
????//判断spring的事务管理是否是激活的,目前我分析结果是在单session情况下,只有OpenSessionInViewFilter调用此方法,才会执行到这里,这种情况下是不会有事务的.?
????//可见下面if块里面的代码是针对非单session,并且有事务的情况处理的.?
????if?(TransactionSynchronizationManager.isSynchronizationActive())&?;{?
????????//?We're?within?a?Spring-managed?transaction,?possibly?from?JtaTransactionManager.?
????????logger.debug("Registering?Spring?transaction?synchronization?for?new?Hibernate?Session");?
????????SessionHolder?holderToUse?=?sessionHolder;?
????????if?(holderToUse?==?null)?{?
????????????holderToUse?=?new?SessionHolder(session);?
????????}?
????????else?{?
????????????holderToUse.addSession(session);?
????????}?
????????if?(TransactionSynchronizationManager.isCurrentTransactionReadOnly())?{?
????????????session.setFlushMode(FlushMode.NEVER);//只读情况下,可以提高效率同时也防止了脏数据等?
????????}?
????????TransactionSynchronizationManager.registerSynchronization(?
????????????????new?SpringSessionSynchronization(holderToUse,?sessionFactory,?jdbcExceptionTranslator,?true));?
????????holderToUse.setSynchronizedWithTransaction(true);//这个变量真是很费解不知道什么情况下会为false呢??
????????if?(holderToUse?!=?sessionHolder)?{//从这里可以看出非单session情况下,有事务也是要绑定session的只是颗粒度不同而已,我猜非单session事务结束后session就被关闭了.?
????????????TransactionSynchronizationManager.bindResource(sessionFactory,?holderToUse);?
????????}?
????}?
????else?{?
????????//?No?Spring?transaction?management?active?->?try?JTA?transaction?synchronization.?
????????registerJtaSynchronization(session,?sessionFactory,?jdbcExceptionTranslator,?sessionHolder);?
????}?

????//?Check?whether?we?are?allowed?to?return?the?Session.?
????//校验能否创建session的动作居然是放在创建session之后处理的,有点不解.?
????if?(!allowCreate?&&?!isSessionTransactional(session,?sessionFactory))?{?
????????closeSession(session);?
????????throw?new?IllegalStateException("No?Hibernate?Session?bound?to?thread,?"?+?
????????????"and?configuration?does?not?allow?creation?of?non-transactional?one?here");?
????}?
????return?session;?
}?

热点排行