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 2.5.6 + JPA + hibernate 3.2.6
- oracle 10g
- jdk 1.6.0_17
As spring helps to manage my transaction, it has been configured as below:
<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.
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.JpaTransactionManager to output more debug information.
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???
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).
h开发者_Python百科ttp://docs.oracle.com/javaee/7/tutorial/doc/persistence-locking002.htm
42.2.2.1 Pessimistic Locking Timeouts
I found the solution JpaTransactionManager does not support transaction timeouts
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.
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.
Here's the docs:
http://static.springsource.org/spring/docs/3.0.5.RELEASE/reference/scheduling.html
精彩评论