java spring 事务超时不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7316157/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 19:30:54  来源:igfitidea点击:

spring transaction timeout doesn't work

javaspringtransactions

提问by Ramon

I am trying to stop/rollback a transaction if it runs too long. But it seems doesn't work by configuring the timeout attribute of spring's transaction manager. My environment:

如果事务运行时间过长,我会尝试停止/回滚事务。但是通过配置spring的事务管理器的超时属性似乎不起作用。我的环境:

  1. spring 2.5.6 + JPA + hibernate 3.2.6
  2. oracle 10g
  3. jdk 1.6.0_17
  1. 弹簧 2.5.6 + JPA + 休眠 3.2.6
  2. 甲骨文 10g
  3. JDK 1.6.0_17

As spring helps to manage my transaction, it has been configured as below:

由于 spring 有助于管理我的事务,它已配置如下:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean 
    below) -->
<tx:advice id="defaultTxAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- Keep SequenceService in a isolation transaction -->
        <tx:method name="get*" read-only="true" />
        <!-- By default, A runtime exception will rollback transaction. -->
        <tx:method name="*" timeout="10" rollback-for="ApplicationException" />
    </tx:attributes>
</tx:advice>

And I have a TicketService which will insert some records to database, simply i let it sleep extra 15 seconds.

而且我有一个 TicketService,它将向数据库插入一些记录,我只是让它多睡 15 秒。

public class DefaultTicketService implements TicketService{
    public void sell() {
        // checking and insert some records to underlying database
        ....
        // sleep to reach the transaction deadline
        try {Thread.sleep(15 * 1000);} catch(Exception e){}
    }
}

Also I amend spring's org.springframework.orm.jpa.JpaTransactionManagerto output more debug information.

另外我修改了 spring 的org.springframework.orm.jpa.JpaTransactionManager来输出更多的调试信息。

protected void doBegin(Object transaction, TransactionDefinition definition) {
    ... ...        
    // Register transaction timeout.
    int timeout = determineTimeout(definition);
    if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
        if (logger.isDebugEnabled()) {
            logger.debug("****setTimeoutinSeconds(" + timeout
                    + " seconds) to EntityManager(" + txObject.getEntityManagerHolder()
                    + "), the transaction begin time:"
                    + new Date(System.currentTimeMillis()));
        }
        txObject.getEntityManagerHolder().setTimeoutInSeconds(timeout);
    }
    ... ... 
}

protected void doCommit(DefaultTransactionStatus status) {
    JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();
    if (status.isDebug()) {
        logger.debug("Committing JPA transaction on EntityManager ["
                + txObject.getEntityManagerHolder().getEntityManager() + "]");
    }
    try {
        if (status.isDebug()) {
            logger.debug("The deadline of entityManager("
                    + txObject.getEntityManagerHolder().getEntityManager() + "):"
                    + txObject.getEntityManagerHolder().getDeadline() + ", and current time:"
                    + new Date(System.currentTimeMillis()));
        }
        EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager()
                .getTransaction();
        tx.commit();
    ... ...
}

After finish running the test, the result is out of my expectation, the transaction is committed at last. Below is the output of the test:

跑完测试,结果出乎我的意料,事务终于提交了。下面是测试的输出:

[JpaTransactionManager] Opened new EntityManager [org.hibernate.ejb.EntityManagerImpl@350225] for JPA transaction
[JpaTransactionManager] ****[Begin]timeout:10 seconds,The deadline of entityManager(org.hibernate.ejb.EntityManagerImpl@350225):null, and current time:Tue Sep 06 15:05:42 CST 2011
[JpaTransactionManager] Exposing JPA transaction as JDBC transaction [SimpleConnectionHandle: com.mchange.v2.c3p0.impl.NewProxyConnection@1eb41d6]
[JpaTransactionManager] Found thread-bound EntityManager [org.hibernate.ejb.EntityManagerImpl@350225] for JPA transaction
... ...
[JpaTransactionManager] Initiating transaction commit
[JpaTransactionManager] Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@350225]
[JpaTransactionManager] ****[Commit]The deadline of entityManager(org.hibernate.ejb.EntityManagerImpl@350225):Tue Sep 06 15:05:52 CST 2011, and current time:Tue Sep 06 15:05:58 CST 2011
[JpaTransactionManager] Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl@350225] after transaction
[EntityManagerFactoryUtils] Closing JPA EntityManager

From the debug information, it is obvious that current time has exceeded deadline, so why spring does't rollback the transaction?? In my understanding, if I set the timeout, such as 10 seconds, Spring will launch a Timer when start a new transaction, if timer reach the time limit, it will rollback the transaction. Could you please tell me why???

从debug信息看,很明显当前时间已经超过deadline了,那么spring为什么不回滚事务呢??在我的理解中,如果我设置了超时时间,比如 10 秒,Spring 会在启动一个新事务时启动一个 Timer,如果计时器达到时间限制,它将回滚事务。你能告诉我为什么吗???

Updated>>

更新>>

When go through tutorial of JavaEE7, found that seem JPA2.1 has provided the support for lock timeout(in general transaction timeout is caused by timeout of obtaining lock).

在翻看JavaEE7教程的时候,发现好像JPA2.1已经提供了对锁超时的支持(一般事务超时是获取锁超时引起的)。

http://docs.oracle.com/javaee/7/tutorial/doc/persistence-locking002.htm

http://docs.oracle.com/javaee/7/tutorial/doc/persistence-locking002.htm

42.2.2.1 Pessimistic Locking Timeouts

42.2.2.1 悲观锁定超时

回答by Mouscellaneous

After some research, I found that the problem lies in the TimerTask thread not using the spring proxies that would normally start and commit the transactions. Theoretically, you could manually code support into your TimerTask to work with the proxy, but that seems like a lot of hassle for me.

经过一番研究,我发现问题在于 TimerTask 线程没有使用通常会启动和提交事务的 spring 代理。从理论上讲,您可以将支持手动编码到 TimerTask 中以使用代理,但这对我来说似乎很麻烦。

Spring has it's own scheduling framework that you can use instead of TimerTask to execute runnables, and won't require much code change to use instead.

Spring 有自己的调度框架,您可以使用它代替 TimerTask 来执行可运行对象,并且不需要更改太多代码即可使用。

Here's the docs:

这是文档:

http://static.springsource.org/spring/docs/3.0.5.RELEASE/reference/scheduling.html

http://static.springsource.org/spring/docs/3.0.5.RELEASE/reference/scheduling.html