一、Q:在"spring与hibernate整合-事务"中,总是出现不存在激活事务的问题,结果去掉<prop key="hibernate.current_session_context_class">thread</prop>但是,为什么呢?
A:那是因为在Spring事务管理中,current Session是绑定到SpringSessionContext中的,而不是ThreadLocalSessionContext中的.
二、hibernate.current_session_context_class常用3种配置:jta,thread,org.springframework.orm.hibernate4.SpringSessionContext
从开Hibernate3.X开始,SessionFactory.getCurrentSession()的后台实现是可拔插的。因此,我们引入了新的扩展接口
(org.hibernate.context.spi.CurrentSessionContext)和新的配置参数(hibernate.current_session_context_class),以便对什么是“当前session”的范围和上下文(scope and context)的定义进行拔插。它定义了单一的方法,currentSession(),特定的实现用它来负责跟踪当前的上下文session。
首先我们看看org.hibernate.context.spi.CurrentSessionContext,这个接口仅有一个方法:
currentSession()表示 根据当前CurrentSessionContext的实现及定义返回”当前的Session”这个接口…Hibernate中有3个类实现了这个接口
All Known Implementing Classes: TASessionContext, ManagedSessionContext, ThreadLocalSessionContext
1: org.hibernate.context.internal.ThreadLocalSessionContext - 当前session通过当前执行的线程来跟踪和界定。
2: org.hibernate.context.internal.JTASessionContext- 当前session根据JTA来跟踪和界定。这和以前的仅支持JTA的方法是完全一样的。
3: org.hibernate.context.internal.ManagedSessionContext..
4.Spring为事务管理,也实现了此接口:
org.springframework.orm.hibernate4.SpringSessionContext –当前Session根据Spring和事务管理器来跟踪和界定.
三、这几种实现都提供了“每个数据库事务对应一个session”的编程模型,也称作每次请求一个session。Hibernate session的起始和终结由数据库事务的生存来控制。
hibernate.current_session_context_class 配置参数定义了应该采用哪个org.hibernate.context.spi.CurrentSessionContext实现?
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 @Transactional声明式事务管理,”currentSession”的定义为: 当前被 Spring事务管理器 管理的Session,此时应配置:
hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext
spring 整合hibernate管理事务后,由Spring的TransactionManager管理事务后, currentSession是绑定到SpringSessionContext的,而不是thread。
此时hibernate.current_session_context_class应该是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()方法:
SessionFactoryImpl 的currentSessionContext属性的实际类型就是
由hibernate.current_session_context_class决定的…
2:首先设置: hibernate.current_session_context_class= org.hibernate.context.internal.ThreadLocalSessionContext
到这一句,currentSessionContext.currentSession()跟进去
现在对于hibernate.current_session_context_class= thread时的getCurrentSession()就很清楚了:
1):尝试取出绑定到线程的Session
2):如果没有,则开启一个”事务提交后自动关闭”的Session,并将此Session加入到ThreadLocal的Map中.
3):返回Session
3:然后再分析: hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext
因为加入了@Transactional,执行addUser()方法时,Spring的TransactionManager会自动Open Sesion,自动开启事务,并且将此Sesion绑定到SpringSessionContext(实际上是TransactionSynchronizationManager的ThreadLocal的Map)中..
然后到SessionFactoryImpl.getCurrentSesssion()的currentSessionContext.currentSession()这一句,跟进去
Object value =TransactionSynchronizationManager.getResource(this.sessionFactory); 关键是这一句,跟进去:
现在对于hibernate.current_session_context_class= org.springframework.orm.hibernate4.SpringSessionContext时的getCurrentSession()就很清楚了:
1): @Transactional声明的方法执行时,Spring的TransactionManager会自动Open Sesion,自动开启事务,并且将此Sesion绑定到 SpringSessionContext(实际上是TransactionSynchronizationManager的ThreadLocal的Map)中..
2):SessionFactory.getCurrentSession()方法执行时,调用SpringSessionContext.currentSession()从TransactionSynchronizationManager的上下文中查找 当前的Session
3):找到后返回当前的Session,找不到,则返回HibernateException("No Sessionfound for current thread")
总结:
hibernate.current_session_context_class=thread(org.hibernate.context.internal.ThreadLocalSessionContext)
与 hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext 时的SessionFactory.getCurrentSession()的不同之处在于:
(1)前者在ThreadLocalSessionContext里的线程局部的Map中查找Session,
(2)而后者在SpringSessionContext的上下文(TransactionSynchronizationManager里的线程局部的Map)中查找...
最终,你会发觉,无论是ThreadLocalSessionContext 或 SpringSessionContext 查找的"currentSession",都是以类似键值对<SessionFactory,Session>的形式存放到ThreadLocal的Map中,也就是说这两者的上下文都是一个ThreadLocal的Map...查找时以SessionFactory为键来查找对应的Session,所以在同一线程中,一个SessionFactory只能有一个currentSession
PS: 从中,我们也知道了,执行SessionFactoryImpl.openSession()时,只是简单地new 一个SessionBuilder,然后调用SessionBuilder.openSession(),得到的Session是不会绑定到任何 org.hibernate.context.spi.CurrentSessionContext 在上下文中。