hibernate 之 getCurrentSession() 详解

来源:互联网 发布:最近淘宝网怎么了 编辑:程序博客网 时间:2024/06/05 08:33

在Spring @Transactional声明式事务管理的配置中,hibernate.current_session_context_class=thread 这一句是不能加的,加了就会出错,那为什么不能加呢?

那是因为在Spring事务管理中,current Session是绑定到SpringSessionContext中的,而不是ThreadLocalSessionContext中的。

 

从hibernate4.0开 始,SessionFactory.getCurrentSession()的后台实现是可拔插的。因此,我们引入了新的扩展接口 (org.hibernate.context.spi.CurrentSessionContext)和新的配置参数(hibernate.current_session_context_class),以便对“当前session”的范围和上下文(scope and context)的定义进行拔插。

CurrentSessionContext接口定义 了单一的方法,currentSession(),特定的实现用它来负责跟踪当前的上下文session。

 

org.hibernate.context.spi.CurrentSessionContext这个接口仅有一个方法:

Session currentSession()  throws HibernateException

Retrieve the current session according to the scoping defined by this implementation.

currentSession()表示 根据当前CurrentSessionContext的实现及定义返回”当前的Session”

 

这个接口Hibernate中有3个类实现了这个接口

JTASessionContext, ManagedSessionContext, ThreadLocalSessionContext

 

1: org.hibernate.context.internal.ThreadLocalSessionContext - 当前session通过当前执行的线程来跟踪和界定。

 

2: org.hibernate.context.internal.JTASessionContext- 当前session根据JTA来跟踪和界定。这和以前的仅支持JTA的方法是完全一样的。

 

3: org.hibernate.context.internal.ManagedSessionContext..

 

Spring为事务管理,也实现了此接口:

org.springframework.orm.hibernate4.SpringSessionContext :当前Session根据Spring和事务管理器来跟踪和界定(即当前session由事务管理器生成:@Transactional 注解或者其他方式配置的事务切入点事务管理器会调用doBegin()等方法生成session并绑定到springSessionContext并开启事务)。

 

hibernate.current_session_context_class 配置参数定义了应该采用哪个org.hibernate.context.spi.CurrentSessionContext实现。

一般而言,此参数的值指明了要使用的实 现类的全名,但那两个内置的实现可以使用简写,即"jta"和"thread"。

hibernate.current_session_context_class=thread

实质是:

hibernate.current_session_context_class= org.hibernate.context.internal.ThreadLocalSessionContext

hibernate.current_session_context_class=jta

实质是:

hibernate.current_session_context_class= org.hibernate.context.internal.JTASessionContext

 

spring 整合hibernate管理事务后,由Spring的TransactionManager管理事务, currentSession是绑定到SpringSessionContext的,而不是thread。

此时hibernate.current_session_context_class应该是org.springframework.orm.hibernate4.SpringSessionContext,它会在使用LocalSessionFactoryBean时自动的设置,所以就不需要你去显示设置current_session_context_class了。



下面我们来分析一下SessionFactoryImpl, 

org.hibernate.context.spi.CurrentSessionContext,

org.hibernate.context.internal.ThreadLocalSessionContext,

org.springframework.orm.hibernate4.SpringSessionContext 这些类的源代码

 

1: 分析sessionFactory.getCurrentSession() 我们跟进去来到SessionFactoryImpl.getCurrentSession()方法:

 

  1. public final class SessionFactoryImpl  
  2.       implements SessionFactoryImplementor {  
  3.   . . .  
  4.   private final transient CurrentSessionContext currentSessionContext;  
  5.   . . .  
  6.   public Session getCurrentSession() throws HibernateException {  
  7.       if ( currentSessionContext == null ) {  
  8.          throw new HibernateException( "No CurrentSessionContext configured!" );  
  9.       }  
  10.       return currentSessionContext.currentSession();  
  11.   }  
  12.    
  13. . . .  
  14. }  

SessionFactoryImpl 的currentSessionContext属性的实际类型就是由hibernate.current_session_context_class决定的。

 

2:首先设置: hibernate.current_session_context_class= org.hibernate.context.internal.ThreadLocalSessionContext

   分析该实现者的代码:

 

  1. public class ThreadLocalSessionContext implements CurrentSessionContext {  
  2.     . . .  
  3.    private static final ThreadLocal<Map> context = newThreadLocal<Map>();  
  4.    . . .  
  5.    
  6.    //打开一个”事务提交后会自动关闭”这种特点的Session  
  7.    protected Session buildOrObtainSession() {  
  8.        return factory.withOptions()  
  9.             .autoClose( isAutoCloseEnabled() )  
  10.             .connectionReleaseMode( getConnectionReleaseMode() )  
  11.             .flushBeforeCompletion( isAutoFlushEnabled() )  
  12.             .openSession();  
  13.     }  
  14.    
  15.     public final Session currentSession() throws HibernateException {  
  16.       //从线程局部量context中尝试取出已经绑定到线程的Session  
  17.       Session current = existingSession( factory );  
  18.        
  19.       //如果没有绑定到线程的Session  
  20.       if (current == null) {  
  21.          //打开一个”事务提交后自动关闭”的这种特点的Session  
  22.          current = buildOrObtainSession();  
  23.             current.getTransaction().registerSynchronization(buildCleanupSynch() );  
  24.          // wrap the session in thetransaction-protection proxy  
  25.          if ( needsWrapping( current ) ) {  
  26.             current = wrap( current );  
  27.          }  
  28.          //将得到的Session绑定到线程中:即以<SessionFactory,Session>键值对方式设置到线程局部量context  
  29.          doBind( current, factory );  
  30.       }  
  31.       return current;  
  32.    }  
  33. . . .  
  34. }  

现在对于hibernate.current_session_context_class= thread时的getCurrentSession()就很清楚了:

1:尝试取出绑定到线程的Session

2:如果没有,则开启一个”事务提交后自动关闭”的这种特点的Session,并将此Session加入到ThreadLocal的Map中.

3:返回Session



3:然后再分析org.springframework.orm.hibernate4.SpringSessionContext代码:

 

  1. Public UserService  
  2. {  
  3.    @Transactional  
  4.    public void addUser(User user) throws Exception  
  5.    {  
  6.       Session session = sessionFactory.getCurrentSession();  
  7.        
  8.       session.save(user);  
  9.    }  
  10. }  

关键:spring的事务管理器会在事务切入点(@Transactional注解的方法或者是其他方式设置的事务切入点,如<tx: 标签设置AOP事务切点)处创建session,自动开启事务,并且将此Sesion绑定到SpringSessionContext(实际上是TransactionSynchronizationManager的ThreadLocal的Map)中。至于事务管理器怎么创建session并绑定到SpringSessionContext中的请看下一篇文章。

如果一个方法中用getCurrentSession()方法获取session,但是该方法不在事务控制范围内(即没用@Transactional注解也不再<tx:标签设置的事务切入点范围),则执行getCurrentSession()方法时就会抛出“NoSession found for current thread"错误!

因为加入了@Transactional,执行addUser()方法时,Spring的TransactionManager会自动创建Sesion,自动开启事务,并且将此Sesion绑定到SpringSessionContext(实际上是TransactionSynchronizationManager的ThreadLocal的Map)中。


分析SpringSessionContext 代码:

[java] view plaincopy
  1. public class SpringSessionContext implements CurrentSessionContext {  
  2.    
  3.    private final SessionFactoryImplementor sessionFactory;  
  4.    
  5.    
  6.    -  - - - - -  
  7.    
  8.    public Session currentSession() throws HibernateException {  
  9.     //关键就是这一句,Spring实际上会去TransactionSynchronizationManager里查找”currentSession”  
  10.    
  11.     Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);  
  12.       if (value instanceof Session) {  
  13.          return (Session) value;  
  14.       }  
  15.       else if (value instanceof SessionHolder) {  
  16.          SessionHolder sessionHolder = (SessionHolder) value;  
  17.          Session session = sessionHolder.getSession();  
  18.          if (TransactionSynchronizationManager.isSynchronizationActive()&&  
  19.                 !sessionHolder.isSynchronizedWithTransaction()) {  
  20.             TransactionSynchronizationManager.registerSynchronization(  
  21.                    new SpringSessionSynchronization(sessionHolder, this.sessionFactory));  
  22.             sessionHolder.setSynchronizedWithTransaction(true);  
  23.     
  24.             FlushMode flushMode = session.getFlushMode();  
  25.             if (FlushMode.isManualFlushMode(flushMode)&&  
  26.                    !TransactionSynchronizationManager.isCurrentTransactionReadOnly()){  
  27.                 session.setFlushMode(FlushMode.AUTO);  
  28.                 sessionHolder.setPreviousFlushMode(flushMode);  
  29.             }  
  30.          }  
  31.          return session;  
  32.       }  
  33.       else if (this.jtaSessionContext != null) {  
  34.          Session session = this.jtaSessionContext.currentSession();  
  35.          if (TransactionSynchronizationManager.isSynchronizationActive()){  
  36.             TransactionSynchronizationManager.registerSynchronization(newSpringFlushSynchronization(session));  
  37.          }  
  38.          return session;  
  39.       }  
  40.       else {  
  41.          throw new HibernateException("No Session found for current thread");  
  42.       }  
  43.    }  
  44.    
  45. }  

Object value =TransactionSynchronizationManager.getResource(this.sessionFactory); 关键是这一句,跟进去:

 

[java] view plaincopy
  1. public abstract class TransactionSynchronizationManager {  
  2.    
  3.  . . .  
  4.  private static final ThreadLocal<Map<Object, Object>> resources;  
  5.    
  6.  public static Object getResource(Object key) {  
  7.       Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);  
  8.       //在ThreadLocal的属性resources里查找Session, resources里以<SessionFactory,SessionHolder>或 <SessionFactory,Session>的键值对存放到ThreadLocal的Map中  
  9.       Object value = doGetResource(actualKey);  
  10.       if (value != null && logger.isTraceEnabled()) {  
  11.          logger.trace("Retrievedvalue [" + value + "] for key [" + actualKey + "] bound to thread [" +  
  12.                 Thread.currentThread().getName() + "]");  
  13.       }  
  14.       return value;  
  15.    }  
  16.    
  17.  . ..  
  18. }  

现在对于hibernate.current_session_context_class= org.springframework.orm.hibernate4.SpringSessionContext时的getCurrentSession()就很清楚了:

 

1:spring的事务管理器会在事务切入点(@Transactional注解的方法或者是其他方式设置的事务切入点,如<tx: 标签设置AOP事务切点)处创建session,自动开启事务,并且将此Sesion绑定到SpringSessionContext(实际上是TransactionSynchronizationManager的ThreadLocal的Map)中。

 

2:SessionFactory.getCurrentSession()方法执行时,调用SpringSessionContext.currentSession()从TransactionSynchronizationManager的上下文中查找 当前的Session 

3:找到后返回当前的Session,找不到,则返回HibernateException("No Session found for current thread")



PS: 从中,我们也知道了,执行SessionFactoryImpl.openSession()时,只是简单地new 一个SessionBuilder,然后调用SessionBuilder.openSession(),得到的Session是不会绑定到任何 org.hibernate.context.spi.CurrentSessionContext 在上下文中的.



总结: hibernate.current_session_context_class=thread(org.hibernate.context.internal.ThreadLocalSessionContext)

与      hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext 时的SessionFactory.getCurrentSession()的不同之处在于: 

 前者找不到session时会创建一个session并绑定到当前线程,而后者找不到session时则会抛出异常HibernateException("No Session found for current thread")


      最终,你会发觉,无论是ThreadLocalSessionContext 或 SpringSessionContext 查找的"currentSession",都是以类似键值对<SessionFactory,Session>的形式存放到ThreadLocal的Map中,也就是说这两者的上下文都是一个ThreadLocal的Map...查找时以SessionFactory为键来查找对应的Session,所以在同一线程中,一个SessionFactory只能有一个currentSession。

0 0
原创粉丝点击