公司下午比较清闲,和同事谈spring如何管理session的事(同事已有三年的j2ee开发经验),起初,我对spring管理session概念很是模糊,不知道spring什么时候从sessionFactory取出session.最初我以为在dao层spring会产生session,但是越想越不能自圆其说,使我产生这种想法的是在我初学hibernate后,可在dao层得到session,而且dao是data access object(数据访问对象),理所应当应该在dao层管理session,但是公司的项目是用spring来管理hibernate的,经过一番请教,同事告诉我由于事务配置在service层,所以当客户端发过来一个请求,spring会在service层管理session和transaction,由于事务的acid特性,所以共用同一个session的service才可以正常回滚,总之,还是有些模糊,所以上网收集了一些资料,分享给和我一样对spring管理session模糊的朋友。
txObject.getSessionHolder().setSynchronizedWithTransaction(true);
session=txObject.getSessionHolder().getSession();
Connectioncon=session.connection();
IntegerpreviousIsolationLevel=DataSourceUtils.prepareConnectionForTransaction(con,definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
if(definition.isReadOnly()&&txObject.isNewSessionHolder()){
//JustsettoNEVERincaseofanewSessionforthistransaction.
session.setFlushMode(FlushMode.NEVER);
}//如果是只读事务,并且sessionholder是新建的,那么就设置hibernate的flushmode为never
if(!definition.isReadOnly()&&!txObject.isNewSessionHolder()){
//WeneedAUTOorCOMMITforanon-read-onlytransaction.
FlushModeflushMode=session.getFlushMode();
if(FlushMode.NEVER.equals(flushMode)){
session.setFlushMode(FlushMode.AUTO);
//如果session的flushmode是nerver,就设置为auto,因为如果事务定义成非readonly,那么这个session一定是可以flush的
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}
}
//AddtheHibernatetransactiontothesessionholder.
txObject.getSessionHolder().setTransaction(session.beginTransaction());//开始一个事务,并把这个事务对象放到sessionholder中,随后这个sessionholder会通过threadlocal放到线程中,以供在commit时使用
//Registertransactiontimeout.
if(definition.getTimeout()!=TransactionDefinition.TIMEOUT_DEFAULT){
txObject.getSessionHolder().setTimeoutInSeconds(definition.getTimeout());//设置超时时间,如果其超时时间为-1,则不进行设置,如果不是-1,那么超时时间是这样设置的newDate(System.currentTimeMillis()+millis*1000);既程序员在配置文件中指定的其实是秒数
}
//RegistertheHibernateSession'sJDBCConnectionfortheDataSource,ifset.
if(getDataSource()!=null){
ConnectionHolderconHolder=newConnectionHolder(con);
if(definition.getTimeout()!=TransactionDefinition.TIMEOUT_DEFAULT){
conHolder.setTimeoutInSeconds(definition.getTimeout());
}
if(logger.isDebugEnabled()){
logger.debug("ExposingHibernatetransactionasJDBCtransaction["+con+"]");
}
TransactionSynchronizationManager.bindResource(getDataSource(),conHolder);
txObject.setConnectionHolder(conHolder);
}
//Bindthesessionholdertothethread.
if(txObject.isNewSessionHolder()){
TransactionSynchronizationManager.bindResource(getSessionFactory(),txObject.getSessionHolder());//如果是新的sessionholder则绑定到线程。这样在进入方法栈中的下一个方法时就能得到整个sessionholder了,connectionholder亦是如此
}
}
catch(Exceptionex){
SessionFactoryUtils.releaseSession(session,getSessionFactory());//如果抛出异常就释放这个session,这个操作还会在后面出现
thrownewCannotCreateTransactionException("CouldnotopenHibernateSessionfortransaction",ex);
}
}
通过以上对代码的注释可以知道,如果给service设置声明式事务管理,假设事务传播途径为required,然后一个service调用另一个service时,他们其实是共用一个session,原则是没有就创建,有就不创建,并返回之前已创建的session和transaction。也就是说spring通过threadlocal把session和对应的transaction放到线程之中,保证了在整个方法栈的任何一个地方都能得到同一个session和transaction。
所以如果你的方法在事务体之内,那么你只要通过hibernatesupportdao或者hibernatetemplate来得到session的话,那这个session一定是开始事务的那个session,这个得到session的主要方法在SessionFactoryUtils里,我们来看一下
(这里还有一个小细节,public abstract class SessionFactoryUtils ,Juergen Hoeller在写工具类的时候为了不能让其有实例使用的是abstract,而我们一般的做法是final类加private的构造方法,看上去不怎么雅观,看看源代码还是能学习到不少写代码的技巧的)
在SessionFactoryUtils的doGetSession里写到,如果当前线程有绑定session,则返回这个session,如果没有绑定session,则看是否允许创建(既allowCreate这个参数是true还是false,这个参数将会在很多地方设计到,比如说hibernatetemplate和hibernatedaosupport里都有),如果不允许创建就抛出一个原始的hibernateException,举个例子,如果你没有给某个service方法配置声明式事务管理,而却要在这个service所调用的dao里得到当前得session,这样就会报这个错了:
if(method.getName().equals("getCurrentSession")){
//HandlegetCurrentSessionmethod:returntransactionalSession,ifany.
try{
returnSessionFactoryUtils.doGetSession((SessionFactory)proxy,false);
//最后一个参数是false,说明这个方法不能返回一个新的session,没有就抛异常
}
catch(IllegalStateExceptionex){
thrownewHibernateException(ex.getMessage());
}
}