在使用Spring+Hibernate的框架时,在applicationContext.xml中配置了如下的代码片段:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource"/>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>
<prop key="hibernate.cache.provider_class">${hibernate.cache.provider_class}</prop>
<!-- 绑定会话到当前线程 -->
<prop key="hibernate.current_session_context_class">${hibernate.current_session_context_class}</prop>
</props>
</property>
<property name="mappingLocations">
<list>
<value>classpath:/com/changetech/model/*.hbm.xml</value>
</list>
</property>
</bean>
在调用Dao(是使用声明式事务管理@Transactional注解来进行配置的)层时,
@Transactional
public void saveOrUpdate(T t)
{
getSession().saveOrUpdate(t);
}
出现以下的错误:
org.hibernate.HibernateException: saveOrUpdate is not valid without active transaction
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:338)
at $Proxy23.saveOrUpdate(Unknown Source)
at com.changetech.dao.impl.BaseDao.saveOrUpdate(BaseDao.java:49)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy15.saveOrUpdate(Unknown Source)
at com.changetech.service.impl.BaseService.saveOrUpdate(BaseService.java:22)
at com.changetech.client.MultiThreadServiceTest.sampleTest(MultiThreadServiceTest.java:51)
at com.changetech.client.MultiThreadServiceTest$ThreadB.runTest(MultiThreadServiceTest.java:66)
at net.sourceforge.groboutils.junit.v1.TestRunnable.run(TestRunnable.java:154)
at java.lang.Thread.run(Thread.java:619)
经过在网上查找原因,说是Hibernate源码里做了一些修改,说是,hibernate里的所有操作(包括get\load\delete\list\saveOrUpdate等)都必须在transcation.isActive()条件下才可以执行,否则就会出现上述的错误。
<prop key="hibernate.current_session_context_class">${hibernate.current_session_context_class}</prop>
如果把这句话去掉之后(Hibernate就开始利用Spring的事务管理),此错误就消失,目前还不知道是为什么。
但是对于spring如何默认打开Transaction还不是很清楚。
外部连接:
If you use spring 2.5 as a drop in replacement @Transactional isn't working any more.
They HibernateSession you get through a currentSession() has no active transaction and result in the following stack trace
Exception in thread "main" org.hibernate.HibernateException: createSQLQuery is not valid without active transaction
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:297)
at $Proxy7.createSQLQuery(Unknown Source)
at ag.pinguin.myservice.impl.MyService.doSomething(MyService.java:30)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:301)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at $Proxy5.doSomething(Unknown Source)
at test.SpringTestMain.main(SpringTestMain.java:16)
With spring 2.0.7 the exact same code/config works fine
As of Spring 2.5, Spring uses a custom CurrentSessionContext implementation plugged into Hibernate (requires Hibernate 3.1+). Previously, Spring created a special SessionFactory proxy (which also worked with Hibernate 3.0.)
Note that your hibernate.cfg.xml specifies
<property name="current_session_context_class">thread</property>
This overrides the default CurrentSessionContext (Spring's) with the standard Hibernate thread-local context. This leads to the failure that you see. Spring 2.5 simply respects your configuration there – in this case, wrong configuration that Spring 2.0 simply ignored. In any case: You shouldn't have specified that even when using Spring 2.0!