Spring: @Transactional @Scheduled方法抛出TransactionException

14

(对Spring还不太熟悉)

我需要一个同时带有 @Scheduled@Transactional 注解的服务方法,以便我可以在其中调用DAO。

声明式事务已启用,事务管理器是基于Hibernate会话工厂的 org.springframework.orm.hibernate3.HibernateTransactionManager

服务类没有实现任何接口,因此使用了CGLIB代理。

一般情况下这个设置很好用(从Web栈即Struts调用的方法),但是当调度程序调用此方法时会引发异常。

以下是相关代码:

服务方法(该类名为ClientWakeAndTerminateManager):

@Scheduled(initialDelay = 5000, fixedRateString = "${rmi.server.threads.clientsScheduleManagement.rate}")
    @Transactional(readOnly = true)
    public void runCheck(){

        //Call a read-only DAO method (the DAO is @Autowired as a class field)

        //do some stuff with the data loaded from DB

    }

我应用程序上下文的相关部分:

<!-- switch on the transactional infrastructure -->
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

    <!-- Utility class to execute transactional code where use of annotation is not possible -->
    <bean class="org.springframework.transaction.support.TransactionTemplate" id="txTemplate">
        <constructor-arg name="transactionManager" ref="transactionManager"/>
    </bean>

    <!-- Transaction manager based on Hibernate -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="hibernateSessionFactory"/>
    </bean>

异常堆栈跟踪:

[ERROR] : Unexpected error occurred in scheduled task.
org.springframework.transaction.TransactionSystemException: Could not commit Hibernate transaction; nested exception is org.hibernate.TransactionException: Transaction not successfully started
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:661)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:755)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:475)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:270)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
    at ch.unine.sitel.lis.rmi.shared.ClientWakeAndTerminateManager$$EnhancerByCGLIB$$d8be4f34.runCheck(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)
Caused by: org.hibernate.TransactionException: Transaction not successfully started
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:127)
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:657)
    ... 22 more

堆栈跟踪显示使用了事务代理,所以我不明白这个异常。求助!

编辑:

我尝试通过以下方式分离 @Transactional@Scheduled 注释:

  • 创建包含 @Scheduled 方法的新类/bean
  • 将我的服务注入到此 bean 中
  • 从原始方法中删除 @Scheduled ,但保留 @Transactional

但我仍然遇到相同的异常。我还尝试在 DAO 方法上放置 @Transactional 并从服务方法中删除它:结果相同。


当已经提交或回滚的事务尝试提交或回滚时,可能会发生这种情况。 - wedens
你能展示一下你的 DAO 方法吗? - wedens
1
好的,我的DAO方法有问题。我开始怀疑它,然后你的评论让我再看一遍。所以这个方法包含了一些残留的手动会话提交(我最近正在现代化和“springify”这个应用程序),所以当事务代理调用提交时,它失败了,因为它已经提交了!感谢您的时间。如果您编辑您的答案提到DAO方法,我会接受它。 - Pierre Henry
2个回答

32

创建一个带有@Transactional注释的方法的单独类,并在您的@Scheduled注释的方法中调用此方法。Spring将通过代理进行调用并正确处理@Transactional

另外,请检查您的DAO方法,确保它不会手动提交或回滚事务。


感谢@wedens提供的答案,对我很有用。您能解释一下为什么在同一个类中方法不起作用吗? - hayat
1
我知道这是一个非常古老的帖子,但我不想让问题悬而未决。原因在于代理的工作方式。最简单的答案是它可以看到进出的内容,但不能看到内部的内容。这是由于代理的本质所致。 - Wes
自那个问题被提出以来已经过去了将近10年。除了创建另一个类并调用其方法之外,现在是否有其他解决方案? - d00d

1
在我的情况下
@EnableTransactionManagement

之前没有使用。在我将此注释添加到我的@Configuration类之一后,它开始工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接