Transaction required exception JPA / Spring

22,338

Solution 1

I changed the annotation-driven config, just by adding proxy-target-class="true" seems to have fixed the issue in one of our environments (ap-southeast) which is Amazon Shanghai, but as for Europe (eu-west), the problem is still happening. This is a nightmare, all the configurations are exactly the same (it just points to different db & s3)

<tx:annotation-driven mode="aspectj" proxy-target-class="true" transaction-manager="transactionManager" />

SOLUTION:

I finally got something, after all. This fixes it (at least apparently).

Reason: Apparently it as something to do with spring initialization, and scheduling some tasks before the initialization has finished, and something got messed up.

I set the transactional annotation at the service layer with a REQUIRES_NEW propagation, to force a new transaction to be created.

@Transactional(propagation = Propagation.REQUIRES_NEW)

Removed the @Transactional from the DAO layer.

I also had to make some changes to the connector, incrementing maxThreads and max/min spare threads.

I also changed all my @Scheduled initialization tasks to start 10 minutes after tomcat start

After all this changes, the error went away.

Notes I also removed the previous change: "proxy-target-class="true"", and it is still working fine, so that weren't really a good fix here, but it might work for you as it did for me on some cases (background tasks).

As as side note, the other change that I had to do to make this work, was to change @Repository to @Component, as some transactions weren't doing writes to the DB on scheduled tasks.

Solution 2

Not an expert in spring, but hope this helps.

Some days ago, reading Spring documentation for a similar issue, I've found:

In particular, you do not need an application server simply for declarative transactions through EJBs. In fact, even if your application server has powerful JTA capabilities, you may decide that the Spring Framework’s declarative transactions offer more power and a more productive programming model than EJB CMT.

AFAIK, in our services, we declare transactions more specific to avoid some of this problems like:

@Transactional
(
    propagation = Propagation.REQUIRED, 
    readOnly = false,
    rollbackFor = Throwable.class
)

If your annotations works ONLY in some servers, try to be specific when declaring it in order to cover your transaction scenario. In this way I guess you will achieve same behaviour in all servers.

Solution 3

I just had the very same problem. Turned out, that I tried to use the class-wide defined EntityManager in a background thread created on the fly, and that caused the exception. There were no other error messages regarding this issue, and the stacktrace pointed to query.executeUpdate(), so it was a bit difficult to sort this out. Going back to serial processing made the error vanish.

Solution 4

I think the issue has been solved in latest versions of spring. Just adding the @Transactional annotation on top of my service class worked for me.

Share:
22,338
Juan Carrey
Author by

Juan Carrey

Passionate software developer.

Updated on June 20, 2020

Comments

  • Juan Carrey
    Juan Carrey almost 4 years

    I have a method in the repository class marked as @Transactional, the aspect is being executed as seen in the stacktrace, but the exception being thrown is "Transaction required exception"

    I changed the @Repository annotation to @Component (and it seemd like it fixed this problem in some situations), but it is still happening on the web role.

    Here is the stacktrace:

    2015-04-13 08:00:56,497 [http-nio-8080-exec-9] WARN  es.mycompany.util.filters.MyFilter - Error storing : /admin/online/update
    org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
            at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:410)
            at org.springframework.orm.jpa.aspectj.JpaExceptionTranslatorAspect.ajc$afterThrowing$org_springframework_orm_jpa_aspectj_JpaExceptionTranslatorAspect$1$18a1ac9(JpaExceptionTranslatorAspect.aj:37)
            at es.mycopmany.dao.MyDAO.updateLastUpdatedTs_aroundBody2(MyDAO.java:36)
            at es.mycopmany.dao.MyDAO$AjcClosure3.run(MyDAO.java:1)
            at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(AbstractTransactionAspect.aj:66)
            at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation(AbstractTransactionAspect.aj:72)
            at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
            at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(AbstractTransactionAspect.aj:70)
            at es.mycompany.dao.MyDAO.updateLastUpdatedTs(MyDAO.java:31)
    

    And here is the code throwing the exception:

        @Transactional
        public void updateLastUpdatedTs(String id, Calendar date) {
            Query query = entityManager.createQuery("update MyEntity set lastUpdatedTs = :ts "
                    + " where id= :id");
            query.setParameter("ts", date);
            query.setParameter("id", id);
            query.executeUpdate();
        }
    

    Transactional annotation comes from org.springframework.transaction.annotation.Transactional

    Versions:

    Spring: 4.1.5.RELEASE 
    Hibernate: 4.3.8.Final 
    Aspectj: 1.8.5 
    Tomcat 8.0.20
    

    Configurations:

    EMF:

    <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
            id="entityManagerFactory">
            <property name="persistenceUnitName" value="athena" />
            <property name="dataSource" ref="dataSource" />
            <property name="jpaDialect">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
            </property>
        </bean>
    

    Transactions:

    <bean class="org.springframework.orm.jpa.JpaTransactionManager"
            id="transactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory" />
        </bean>
    
        <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager" />
    

    I am going truly nuts with this, any help would be great.

    As a note, this all works perfectly fine on my development environment (Windows, Idea Tomcat 8, JDK 8.0.31 (Oracle's), but it raises this error on Amazon EC2 Elasticbeanstalk (Tomcat 8, 64bit Amazon Linux 2015.03, Open JDK 8.0.31 (Tried to use 8.0.40 from Oracle as well)

    Edit: A bit more info: The exception is thrown on a Filter, at the end of the whole filter chain.

    Here is some debug info before the exception:

    2015-04-13 14:57:48,578 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Creating new transaction with name [MyService.myMethod]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
    2015-04-13 14:57:48,578 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@33f67ee5] for JPA transaction
    2015-04-13 14:57:48,580 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@3112368a]
    2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@3193771b] for key [HikariDataSource (HikariPool-1)] to thread [http-bio-8080-exec-7]
    2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.orm.jpa.EntityManagerHolder@497d4e44] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@5019da97] to thread [http-bio-8080-exec-7]
    2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
    2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.aspectj.AnnotationTransactionAspect - Getting transaction for [MyService.myMethod]
    2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@497d4e44] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@5019da97] bound to thread [http-bio-8080-exec-7]
    2015-04-13 14:57:48,581 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@33f67ee5] for JPA transaction
    2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@3193771b] for key [HikariDataSource (HikariPool-1)] bound to thread [http-bio-8080-exec-7]
    2015-04-13 14:57:48,581 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Participating in existing transaction
    2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.aspectj.AnnotationTransactionAspect - Getting transaction for [MyDao.updateLastUpdatedTs]
    2015-04-13 14:57:48,581 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Opening JPA EntityManager
    2015-04-13 14:57:48,582 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Registering transaction synchronization for JPA EntityManager
    2015-04-13 14:57:48,582 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.orm.jpa.EntityManagerHolder@4f83552c] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@7cc8111c] to thread [http-bio-8080-exec-7]
    2015-04-13 14:57:48,608 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@4f83552c] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@7cc8111c] bound to thread [http-bio-8080-exec-7]
    2015-04-13 14:57:48,608 [http-bio-8080-exec-7] TRACE org.springframework.transaction.aspectj.AnnotationTransactionAspect - Completing transaction for [MyDao.updateLastUpdatedTs] after exception: org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
    

    Which actually says, it has created the Transaction, it then joined the transaction (There are two @Transactionals now, one at the service layer, and the other at the DAO layer), and then it rollsback the transaction, due to an exception "Transaction required".

    This is nuts.

    EDIT Well, I found this line of debug:

    2015-04-13 15:27:44,074 [http-bio-8080-exec-2] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Participating transaction failed - marking existing transaction as rollback-only

    The values here are: propagation=REQUIRED, isolation=DEFAULT

    Seems that, there is a transaction, which was checked as completed, and joining the transaction failed, so it marks it as rollback only, because it could not join it.

  • Juan Carrey
    Juan Carrey about 9 years
    We don't use JTA. And @Transactional annotation has some defaults already. The annotation works, as it fires the AnnotationSupportAspect, but still, the exception is thrown. Thanks for helping though
  • Lucky
    Lucky about 9 years
    you can accept your own answer. +1 for posting the answer.
  • Juan Carrey
    Juan Carrey about 9 years
    It won't allow me, it says can't in 2 days. But anyway, this has fixed the issue for our Asia environment (ap-southeast) but NOT for Europe (eu-west) which is driving me very crazy. All the configurations are exactly the same.
  • chrylis -cautiouslyoptimistic-
    chrylis -cautiouslyoptimistic- about 9 years
    Including the exact configuration of the underlying SQL database?
  • Juan Carrey
    Juan Carrey about 9 years
    Yes, different instances (one in each region), different sizes (instance sizes), but same RDS with same parameter groups (same parameters). The database is not the problem as described in the solution. But thanks.
  • Juan Carrey
    Juan Carrey about 5 years
    Definetly my issue had to do with background threads and waiting for initialization.
  • Juan Carrey
    Juan Carrey almost 4 years
    Bear in mind that the error relies on scheduled task triggering before spring initialized. Maybe it was fixed on newer versions. I can not really tell, this was long ago already. Some transactions were working fine, and also it was happening in AWS only. Maybe it was due to their coretto jdk, I am not sure now.