Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JpaTransactionManager does not support transaction timeouts [SPR-5195] #9868

Closed
spring-projects-issues opened this issue Oct 2, 2008 · 10 comments
Assignees
Labels
type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

Jacques Couzteau opened SPR-5195 and commented

Transactions do not time out even the time out is specified. I use
org.springframework.orm.jpa.JpaTransactionManager

I tried annotations on methods, i.e.
@Transactional(readOnly = false, timeout = 1)
public interface MyService {...}

as well as the property on the bean:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="defaultTimeout" value="1"/>
</bean>

The timeout never triggers a rollback.

Also see this thread: http://forum.springframework.org/showthread.php?t=60517

My EntityManagerFactoryBean:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="jpaProperties">
<util:properties location="classpath:com/adobe/codex/dao/jpa/hibernate.properties"/>
</property>
</bean>

my datasource, but I also tried with other datasources in order to run independent of JBoss, i.e. Tomcat and jetty.
<jee:jndi-lookup id="dataSource" jndi-name="java:CodexDS" />


Affects: 2.5.5

Referenced from: commits 45dc856

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

This is a known limitation, actually: JPA simply doesn't support the notion of a timeout, so Spring cannot propagate a defined timeout to the persistence provider. In general, a defined timeout is just a "hint" to the runtime system and not guaranteed to apply in any kind of strict fashion. Stricter guarantees are only provided by specific backends there, so are completely up the concrete backend in use.

So it very much depends on the backend transaction manager and persistence provider whether timeouts will actually be respected. HibernateTransactionManager for example does support timeouts (although just through the JDBC query timeout facility, which in turn gets ignored by some JDBC drivers). JtaTransactionManager supports timeouts as well, with many JTA providers asynchronously cancelling transactions after a timeout.

So we could research whether Hibernate's timeout facility could somehow be supported in Spring's HibernateJpaDialect, which goes beyond the standard JPA API anyway. This is certainly worth doing in the Spring 3.0 timeframe. My recommendation for the time being would be to use Spring's JtaTransactionManager backed by a JTA provider (and check whether the latter actually handles timeouts as desired).

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Jacques Couzteau commented

Thanks for your comments, Juergen. Very much appreciated.

jacques

@spring-projects-issues
Copy link
Collaborator Author

Jacques Couzteau commented

On a side note: It would probably not hurt to include this information in the documentation.

@spring-projects-issues
Copy link
Collaborator Author

Jacques Couzteau commented

To finish this off: replaced my JpaTransacrion Manager bean (see bug) with
<tx:jta-transaction-manager />.
That was basically all there was too it. I did have to resolve some quite painful classpath issue to be able to build, test and to run it in JBoss. The pain point was the inclusion of jta-1.0.1B.jar in my war which was introduced by

<dependency>
     <groupId>org.hibernate</groupId>
     <artifactId>hibernate-annotations</artifactId>
     <version>3.2.1.ga</version>
</dependency>

Adding the scope to provided did finally do the trick: <scope>provided</scope>

@spring-projects-issues
Copy link
Collaborator Author

Chris Borrill commented

If you do not want (or need) to use the JtaTransactionManager class and install a transaction manager (such as JOTM), then the following work-around works for me:

Sub-class org.springframework.orm.jpa.vendor.HibernateJpaDialect and over-ride the beginTransaction method as follows. This works by delegating transaction timeout to Hibernate which ultimately calls setQueryTimeout() on the JDBC statmenent.

class CustomHibernateJpaDialect extends HibernateJpaDialect {

private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;

@Override
public Object beginTransaction(final EntityManager entityManager,
        final TransactionDefinition definition) throws SQLException {
    if (defaultTimeout != TransactionDefinition.TIMEOUT_DEFAULT) {
        getSession(entityManager).getTransaction().setTimeout(
                defaultTimeout);
    }
    return super.beginTransaction(entityManager, definition);
}

public void setDefaultTimeout(final int defaultTimeout) {
    this.defaultTimeout = defaultTimeout;
}

}

Spring context look like this:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="mine"/>
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaDialect">
        <bean class="com.xxxx.CustomHibernateJpaDialect" >
            <!-- 
                The maximum time a transaction is allowed to run for before being terminated
            -->
            <property name="defaultTimeout" value="${database.transaction.timeout}"/>
        </bean>
    </property>
</bean> 

@spring-projects-issues
Copy link
Collaborator Author

Andrew C. Oliver commented

The problem is that the EntityManagerFactoryUtils has a EntityManagerSynchronization and it closes the entity manager. Then a flush is triggered but because the entity manager is closed, a completely different em is opened to flush, but since it is totally empty nothing happens. I think the synchronization should flush and then close.

@spring-projects-issues
Copy link
Collaborator Author

Andrew C. Oliver commented

disregard i meant to comment on a different bug

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

In Spring 3.0, JpaTransactionManager now exposes the resolved timeout (respecting the "defaultTimeout" setting on JpaTransactionManager as well) to JpaDialect's beginTransaction. HibernateJpaDialect in turn applies the given timeout onto the native Hibernate Transaction before the begin call, as suggested above. This will be processed as a per-transaction timeout similar to JTA.

Additionally, we also expose the JPA 2.0 timeout properties to JPA Query objects before executing them. This is a hint to the JPA provider that it may apply to underlying JDBC statements derived from the JPA query. While this is query-level only and may also simply get ignored by the provider, it is a standard JPA 2.0 way of applying timeouts - which Spring 3.0 is supporting as well.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Jacques Couzteau commented

Thanks Juergen!

@spring-projects-issues
Copy link
Collaborator Author

Jacques Couzteau commented

Just updated to spring 3.0.0.RELEASE.

Interestingly I now see the following error when querying the database:
2010-01-08 01:14:03,310 DEBUG [org.springframework.ws.transport.http.MessageDispatcherServlet] - Could not complete request
java.lang.NoSuchMethodError: org.springframework.orm.jpa.JpaTransactionManager.determineTimeout(Lorg/springframework/transaction/TransactionDefinition;)I
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:331)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:315)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:257)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:102)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)

Is this related to the change. Am I missing something?

My transaction manager is this:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

The transaction manager on the path is the one from 3.0 and in fact it does not have the method.

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants