Question

I try to get the TaskExecutor (ThreadPoolTaskExecutor) to work for a currently well working service with Transactions but when i start the Task the existing Transaction is not found anymore.

(When below start() Method is called there is already a transaction active) (The SimplePOJO ist out of context and just holds some data lets say to be stored in the DB)

@Component
@Transactional(rollbackFor = { Exception.class })
public class MyService {

    @Autowired
    MyTaskRunner myTaskRunner;

    public void start() {

        // TransactionSynchronizationManager.isActualTransactionActive() -> true

        SimplePOJO pojo = new SimplePOJO();
        myTaskRunner.executeTask(pojo);
    }
}

Above Service uses this Component:

@Component
public class MyTaskRunner implements ApplicationContextAware {

    @Autowired
    private TaskExecutor taskExecutor;

    private static ApplicationContext applicationContext = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        MyTaskRunner.applicationContext = applicationContext;
    }

    public void executeTask(SimplePOJO simplePOJO) {

        // TransactionSynchronizationManager.isActualTransactionActive() -> true

        MyDelegate myDelegate = applicationContext.getBean(MyDelegate.class);
        myDelegate.setSimplePOJO(simplePOJO);
        taskExecutor.execute(myDelegate);
    }
}

That gets a ThreadPoolTaskExecutor configured this way:

<bean id="taskExecutor"
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="1" />
    <property name="maxPoolSize" value="5" />
    <property name="queueCapacity" value="25" />
</bean>

And should finally start the Async work in a new transaction per task:

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class MyDelegate implements Runnable {

    private SimplePOJO simplePOJO;

    @Override
    public void run() {

        // TransactionSynchronizationManager.isActualTransactionActive()); -> False!

        if (this.simplePOJO != null) {
            this.doStuff(angebotAnfrageContainer);
        }
    }

    public void setSimplePOJO(SimplePOJO simplePOJO) {
        this.simplePOJO = simplePOJO;
    }

    @Async
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private void doStuff() {

        // TransactionSynchronizationManager.isActualTransactionActive()); -> False!

        // do stuff with the pojos data in the transaction (persist on DB)
    }

}

Do you have any suggestions what i am missing here?

Very many thanks in advance!

What i tried Wrote above code & searched the web for hours, this is what i found out:

I have to admit i am very new to Spring and havent yet understood the framework too well. Within the last few hours of trying and searching the web i think it has something to do with Proxys beeing created from spring and that chain breaks when i call the execute()-Method or when the TaskRunner calls the run()-Method that instead of calling the Proxy calls the Implementation (MyDelegate) directly ... but i have absolutly no idear how i could verify or change that.

Very many thanks in advance

Was it helpful?

Solution

Transactions are applied using AOP. Spring, by default, uses proxies to apply AOP. The usage of proxies result in only calls into (external method calls) the object are intercepted. In your case you are doing a method call from inside the object which will not pass through the proxy and will not be intercepted, basically rendering your @Transactional useless.

Simply moving the @Transactional to the run method should fix your problem as the run method will now run in a transaction.

@Transactional
public void run() {...}

You can also remove the @Async as that doesn't do anything here, you are executing task with a TaskExecutor already making them async (depending on which TaskExecutor you use).

Links:

  1. Understanding AOP Proxies
  2. TaskExecutor
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top