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过滤器,用来把一个HibernateSession和一次完整的请求过程对应的线程相绑定。目的是为了实现"OpenSessioninView"的模式。例如:它允许在事务提交之后延迟加载显示所需要的对象。

下面从处理请求的入口读起,下面所指的session均为hibernatesession不再特别说明,本文观点纯属个人观点如有错误请批评指正不胜感激.

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

publicfinalvoiddoFilter

(ServletRequestrequest,ServletResponseresponse,FilterChainfilterChain)

throwsServletException,IOException{

//首选判断进行过滤的是否是http请求

if(!(requestinstanceofHttpServletRequest)||!(responseinstanceofHttpServletResponse)){

thrownewServletException("OncePerRequestFilterjustsupportsHTTPrequests");

}

//如果是http请求的话进行强转

HttpServletRequesthttpRequest=(HttpServletRequest)request;

HttpServletResponsehttpResponse=(HttpServletResponse)response;

//alreadyFilteredAttributeName是一个标识,用于判断是否需要进行OpenSessionInViewFilter

StringalreadyFilteredAttributeName=getAlreadyFilteredAttributeName();

if(request.getAttribute(alreadyFilteredAttributeName)!=null||shouldNotFilter(httpRequest)){

//Proceedwithoutinvokingthisfilter...

filterChain.doFilter(request,response);

}

else{

//Doinvokethisfilter...

request.setAttribute(alreadyFilteredAttributeName,Boolean.TRUE);

//下面这个方法是abstract方法由OpenSessionInViewFilter实现,是OpenSessionInViewFilter的核心方法

doFilterInternal(httpRequest,httpResponse,filterChain);

}

}

好下面让我们进入doFilterInternal一探究竟

protectedvoiddoFilterInternal(HttpServletRequestrequest,

HttpServletResponseresponse,FilterChainfilterChain)

throwsServletException,IOException{

/**

*从spring的上下文中取得SessionFactory对象

*WebApplicationContextwac=WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());

*return(SessionFactory)wac.getBean(getSessionFactoryBeanName(),SessionFactory.class);

*getSessionFactoryBeanName()方法默认返回"sessionFactory"字符串,在spring配置文件中可要注意了,别写错了.

*/

SessionFactorysessionFactory=lookupSessionFactory(request);

booleanparticipate=false;//标识过滤器结束时是否进行关闭session等后续处理

if(isSingleSession()){//单session模式

//判断能否在当前线程中取得sessionFactory对象对应的session

if(TransactionSynchronizationManager.hasResource(sessionFactory)){

//当能够找到session的时候证明会有相关类处理session的收尾工作,这个过滤器不能进行关闭session操作,否则会出现重复关闭的情况.

participate=true;//但我并没有想出正常使用的情况下什么时候会出现这种情况.

}else{

Sessionsession=getSession(sessionFactory);//当前线程取不到session的时候通过sessionFactory创建,下面还会详细介绍此方法

//将session绑定到当前的线程中,SessionHolder是session的一层封装,里面有个存放session的map,而且是线程同步的Collections.synchronizedMap(newHashMap(1));

//但是单session模式下一个SessionHolder对应一个session,核心方法是getValidatedSession取得一个open状态下的session,并且取出后从map中移出.

TransactionSynchronizationManager.bindResource(sessionFactory,

;newSessionHolder(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等)的线程是采用线程池机制的,线程使用过后并不会销毁.

SessionHoldersessionHolder=(SessionHolder)TransactionSynchronizationManager

.unbindResource(sessionFactory);

//取消绑定只是取消session对象和线程对象之间的引用,还没有关闭session,不关闭session相对于不关闭数据库连接,所以这里要关闭session

closeSession(sessionHolder.getSession(),sessionFactory);

}else{

//非单session模式,没有研究.

SessionFactoryUtils.processDeferredClose(sessionFactory);

}

}

}

}

下面详细介绍TransactionSynchronizationManager的几个关键的方法

publicabstractclassTransactionSynchronizationManager{

//线程局部变量,为每一个使用该变量的线程都提供一个变量值的副本,每一个线程都可以独立地改变自己的副本,而不会与其它线程的副本冲突,用于存放session

privatestaticfinalThreadLocalresources=newThreadLocal();

publicstaticbooleanhasResource(Objectkey){//判断当前线程是否已经绑定了session,key是sessionFactory对象,一个sessionFactory可以绑定一个session

Assert.notNull(key,"Keymustnotbenull");//spring的Assert类不错,大家可以看看很简单

Mapmap=(Map)resources.get();

return(map!=null&&map.containsKey(key));

}

publicstaticvoidbindResource(Objectkey,Objectvalue)throwsIllegalStateException{//绑定session到当前线程

Assert.notNull(key,"Keymustnotbenull");

Assert.notNull(value,"Valuemustnotbenull");

Mapmap=(Map)resources.get();//ThreadLocal对象只可以存放一个对象,所以使用map来扩展

if(map==null){

map=newHashMap();

resources.set(map);

}

if(map.containsKey(key)){

thrownewIllegalStateException("Alreadyvalue["+map.get(key)+"]forkey["+key+

"]boundtothread["+Thread.currentThread().getName()+"]");

}

map.put(key,value);

}

staticObjectunbindResource(Objectkey)throwsIllegalStateException{//取消当前线程对session的绑定

Assert.notNull(key,"Keymustnotbenull");

Mapmap=(Map)resources.get();

if(map==null||!map.containsKey(key)){

thrownewIllegalStateException(

"Novalueforkey["+key+"]boundtothread["+Thread.currentThread().getName()+"]");

}

Objectvalue=map.remove(key);

if(map.isEmpty()){

resources.set(null);

}

returnvalue;

}

另外一个非常关键的方法是OpenSessionInViewFilter的getSession方法,我们看这个方法的关键并不是如何取得session,而且注意这里设置了FlushMode

protectedSessiongetSession(SessionFactorysessionFactory)

throwsDataAccessResourceFailureException{

Sessionsession=SessionFactoryUtils.getSession(sessionFactory,true);

FlushModeflushMode=getFlushMode();//默认情况下是FlushMode.NEVER

if(flushMode!=null){

session.setFlushMode(flushMode);

}

returnsession;

}

读到这个地方,大家对ThreadLocal感兴趣的话,可以看下我以前写的一篇文章http://blog.csdn.net/sunyujia/archive/2008/06/15/2549564.aspx

FlushMode.NEVER:

调用Session的查询方法时,不清理缓存

调用Session.commit()时,不清理缓存

调用Session.flush()时,清理缓存

不过FlushMode.NEVER已经不再建议使用了

官方描述如下

Deprecated.useMANUALinstead.使用FlushMode.MANUAL来代替

TheSessionisneverflushedunlessSession.flush()isexplicitlycalledbytheapplication.Thismodeisveryefficientforreadonlytransactions.

直到调用Session.flush()才会将变化反应到数据库,在只读的情况下是效率非常高的

详见http://www.hibernate.org/hib_docs/v3/api/org/hibernate/FlushMode.html

我们来细看SessionFactoryUtils.getSession(sessionFactory,true);

publicstaticSessiongetSession(SessionFactorysessionFactory,booleanallowCreate)throwsDataAccessResourceFailureException,IllegalStateException{

try{

returndoGetSession(sessionFactory,null,null,allowCreate);

}

catch(HibernateExceptionex){

thrownewDataAccessResourceFailureException("CouldnotopenHibernateSession",ex);

}

}

从上面可以看出doGetSession才是真正的核心方法,这里非常重要的是HibernateTemplate也是调用此方法

protectedSessiongetSession(){

if(isAlwaysUseNewSession()){

returnSessionFactoryUtils.getNewSession(getSessionFactory(),getEntityInterceptor());

}elseif(!isAllowCreate()){

returnSessionFactoryUtils.getSession(getSessionFactory(),false);

}else{

returnSessionFactoryUtils.getSession(

getSessionFactory(),getEntityInterceptor(),getJdbcExceptionTranslator());

}

}

无论如何调用最后都是落实到SessionFactoryUtils.doGetSession这个方法上面,无论这个方法最后是否返回了null实际上在方法中都取得了session,

这是整个流程中最复杂的一个方法,部分代码我理解的也很不好,大家一起研究研究.

privatestaticSessiondoGetSession(

SessionFactorysessionFactory,InterceptorentityInterceptor,

SQLExceptionTranslatorjdbcExceptionTranslator,booleanallowCreate)

throwsHibernateException,IllegalStateException{

Assert.notNull(sessionFactory,"NoSessionFactoryspecified");

//取当前线程绑定

的session

SessionHoldersessionHolder=(SessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);

//sessionHolder就可以看成是当前线程绑定的session了

if(sessionHolder!=null&&!sessionHolder.isEmpty()){

Sessionsession=null;

if(TransactionSynchronizationManager.isSynchronizationActive()&

sessionHolder.doesNotHoldNonDefaultSession()){//判断spring的事务管理是否是激活的,同时SessionHolder对象中有且仅有一个session

//Springtransactionmanagementisactive->

//registerpre-boundSessionwithitfortransactionalflushing.

session=sessionHolder.getValidatedSession();

//在开事务的时候HibernateTransactionManager类中的doBegin方法会将isSynchronizedWithTransaction设置为true,暂时不知道什么情况下会进入如下代码块

if(session!=null&&!sessionHolder.isSynchronizedWithTransaction()){

TransactionSynchronizationManager.registerSynchronization(

newSpringSessionSynchronization(sessionHolder,sessionFactory,jdbcExceptionTranslator,false));

sessionHolder.setSynchronizedWithTransaction(true);

//SwitchtoFlushMode.AUTO,aswehavetoassumeathread-boundSession

//withFlushMode.NEVER,whichneedstoallowflushingwithinthetransaction.

FlushModeflushMode=session.getFlushMode();

if(flushMode.lessThan(FlushMode.COMMIT)&

!TransactionSynchronizationManager.isCurrentTransactionReadOnly()){

session.setFlushMode(FlushMode.AUTO);

sessionHolder.setPreviousFlushMode(flushMode);

}

}

}

else{

//NoSpringtransactionmanagementactive->tryJTAtransactionsynchronization.

//在没用事务的情况下下面的方法中只调用了sessionHolder.getValidatedSession();

session=getJtaSynchronizedSession(sessionHolder,sessionFactory,jdbcExceptionTranslator);

}

if(session!=null){

returnsession;//在使用了OpenSessionInViewFilter的情况下,HibernateTemplate执行此方法会在这里return

}

}

//如果当前线程没有绑定session那么无论如何都是要创建session的,但是否会return还要取决于allowCreate等条件,在后面会看到

Sessionsession=(entityInterceptor!=null?

sessionFactory.openSession(entityInterceptor):sessionFactory.openSession());

//UsesameSessionforfurtherHibernateactionswithinthetransaction.

//Threadobjectwillgetremovedbysynchronizationattransactioncompletion.

//判断spring的事务管理是否是激活的,目前我分析结果是在单session情况下,只有OpenSessionInViewFilter调用此方法,才会执行到这里,这种情况下是不会有事务的.

//可见下面if块里面的代码是针对非单session,并且有事务的情况处理的.

if(TransactionSynchronizationManager.isSynchronizationActive())&;{

//We'rewithinaSpring-managedtransaction,possiblyfromJtaTransactionManager.

logger.debug("RegisteringSpringtransactionsynchronizationfornewHibernateSession");

SessionHolderholderToUse=sessionHolder;

if(holderToUse==null){

holderToUse=newSessionHolder(session);

}

else{

holderToUse.addSession(session);

}

if(TransactionSynchronizationManager.isCurrentTransactionReadOnly()){

session.setFlushMode(FlushMode.NEVER);//只读情况下,可以提高效率同时也防止了脏数据等

}

TransactionSynchronizationManager.registerSynchronization(

newSpringSessionSynchronization(holderToUse,sessionFactory,jdbcExceptionTranslator,true));

holderToUse.setSynchronizedWithTransaction(true);//这个变量真是很费解不知道什么情况下会为false呢?

if(holderToUse!=sessionHolder){//从这里可以看出非单session情况下,有事务也是要绑定session的只是颗粒度不同而已,我猜非单session事务结束后session就被关闭了.

TransactionSynchronizationManager.bindResource(sessionFactory,holderToUse);

}

}

else{

//NoSpringtransactionmanagementactive->tryJTAtransactionsynchronization.

registerJtaSynchronization(session,sessionFactory,jdbcExceptionTranslator,sessionHolder);

}

//CheckwhetherweareallowedtoreturntheSession.

//校验能否创建session的动作居然是放在创建session之后处理的,有点不解.

if(!allowCreate&&!isSessionTransactional(session,sessionFactory)){

closeSession(session);

thrownewIllegalStateException("NoHibernateSessionboundtothread,"+

"andconfigurationdoesnotallowcreationofnon-transactionalonehere");

}

returnsession;

}

相关推荐