技术开发 频道

Spring声明式事务管理源码解读


代码
 
  1. txObject.getSessionHolder().setSynchronizedWithTransaction(true);   
  2.             session = txObject.getSessionHolder().getSession();   
  3.   
  4.             Connection con = session.connection();   
  5.             Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);   
  6.             txObject.setPreviousIsolationLevel(previousIsolationLevel);   
  7.   
  8.             if (definition.isReadOnly() && txObject.isNewSessionHolder()) {   
  9.                 // Just set to NEVER in case of a new Session for this transaction.   
  10.                 session.setFlushMode(FlushMode.NEVER);   
  11.             }//如果是只读事务,并且sessionholder是新建的,那么就设置hibernate的flushmode为never   
  12.   
  13.             if (!definition.isReadOnly() && !txObject.isNewSessionHolder()) {   
  14.                 // We need AUTO or COMMIT for a non-read-only transaction.   
  15.                 FlushMode flushMode = session.getFlushMode();   
  16.                 if (FlushMode.NEVER.equals(flushMode)) {   
  17.                     session.setFlushMode(FlushMode.AUTO);   
  18. //如果session的flushmode是nerver,就设置为auto,因为如果事务定义成非readonly,那么这个session一定是可以flush的   
  19.                     txObject.getSessionHolder().setPreviousFlushMode(flushMode);   
  20.                 }   
  21.             }   
  22.   
  23.             // Add the Hibernate transaction to the session holder.   
  24.             txObject.getSessionHolder().setTransaction(session.beginTransaction());//开始一个事务,并把这个事务对象放到sessionholder中,随后这个sessionholder会通过threadlocal放到线程中,以供在commit时使用   
  25.   
  26.             // Register transaction timeout.   
  27.             if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {   
  28.                 txObject.getSessionHolder().setTimeoutInSeconds(definition.getTimeout());//设置超时时间,如果其超时时间为-1,则不进行设置,如果不是-1,那么超时时间是这样设置的new Date(System.currentTimeMillis() + millis*1000);既程序员在配置文件中指定的其实是秒数   
  29.             }   
  30.   
  31.             // Register the Hibernate Session's JDBC Connection for the DataSource, if set.   
  32.             if (getDataSource() != null) {   
  33.                 ConnectionHolder conHolder = new ConnectionHolder(con);   
  34.                 if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {   
  35.                     conHolder.setTimeoutInSeconds(definition.getTimeout());   
  36.                 }   
  37.                 if (logger.isDebugEnabled()) {   
  38.                     logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]");   
  39.                 }   
  40.                 TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);   
  41.                 txObject.setConnectionHolder(conHolder);   
  42.             }   
  43.   
  44.             // Bind the session holder to the thread.   
  45.             if (txObject.isNewSessionHolder()) {   
  46.                 TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());//如果是新的sessionholder则绑定到线程。这样在进入方法栈中的下一个方法时就能得到整个sessionholder了,connectionholder亦是如此   
  47.             }   
  48.         }   
  49. catch (Exception ex) {   
  50.             SessionFactoryUtils.releaseSession(session, getSessionFactory());//如果抛出异常就释放这个session,这个操作还会在后面出现   
  51.             throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);   
  52.         }   
  53.     }  
  通过以上对代码的注释可以知道,如果给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的构造方法,看上去不怎么雅观,看看源代码还是能学习到不少写代码的技巧的,这里还有一个插曲,上次feiing还说java为什么不能弄成final和abstract同时存在呢,这样就可以确保既不会有实例产生,也不能继承了,呵呵)

  在SessionFactoryUtils的doGetSession里写到,如果当前线程有绑定session,则返回这个session,如果没有绑定session,则看是否允许创建(既allowCreate这个参数是true还是false,这个参数将会在很多地方设计到,比如说hibernatetemplate和hibernatedaosupport里都有),如果不允许创建就抛出一个原始的hibernateException,举个例子,如果你没有给某个service方法配置声明式事务管理,而却要在这个service所调用的dao里得到当前得session,这样就会抛这个错了:
代码
 
  1. if (method.getName().equals("getCurrentSession")) {   
  2.                 // Handle getCurrentSession method: return transactional Session, if any.   
  3.                 try {   
  4.                     return SessionFactoryUtils.doGetSession((SessionFactory) proxy, false);   
  5. //最后一个参数是false,说明这个方法不能返回一个新的session,没有就抛异常   
  6.                 }   
  7.                 catch (IllegalStateException ex) {   
  8.                     throw new HibernateException(ex.getMessage());   
  9.                 }   
  10.             }  

到这里事务开始部分基本就结束了
按正常流程,那么接下来就是方法结束commit的问题了。Commit放到下一篇文章里说吧
0
相关文章