Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling @Transactional methods from another thread (Runnable)

Tags:

Is there any simple solution to save data into database using JPA in a new thread?

My Spring based web application allows user to manage scheduled tasks. On runtime he can create and start new instances of predefined tasks. I am using spring's TaskScheduler and everything works well.

But I need to save boolean result of every fired task into database. How can I do this?

EDIT: I have to generalize my question: I need to call a method on my @Service class from tasks. Because the task result has to be "processed" before saving into database.

EDIT 2: Simplified version of my problematic code comes here. When saveTaskResult() is called from scheduler, message is printed out but nothing is saved into db. But whenever I call saveTaskResult() from controller, record is correctly saved into database.

@Service
public class DemoService {

    @Autowired
    private TaskResultDao taskResultDao;

    @Autowired
    private TaskScheduler scheduler;

    public void scheduleNewTask() {
        scheduler.scheduleWithFixedDelay(new Runnable() {

            public void run() {
                // do some action here
                saveTaskResult(new TaskResult("result"));
            }

        }, 1000L);
    }

    @Transactional
    public void saveTaskResult(TaskResult result) {
        System.out.println("saving task result");
        taskResultDao.persist(result);
    }

}
like image 239
Ondřej Míchal Avatar asked Jun 30 '12 15:06

Ondřej Míchal


People also ask

What happens if one @transactional annotated method is calling another @transactional annotated method on the same object instance?

1 Answer. Show activity on this post. If you call method2() from method1() within the same class, the @Transactional annotation of the second method will not have any effect because it is not called through proxy, but directly.

Does @transactional work on private methods?

The answer your question is no - @Transactional will have no effect if used to annotate private methods. The proxy generator will ignore them. When using proxies, you should apply the @Transactional annotation only to methods with public visibility.

How does @transactional work internally?

The transactional annotation itself defines the scope of a single database transaction. The database transaction happens inside the scope of apersistence context. The persistence context is in JPA the EntityManager , implemented internally using an Hibernate Session (when using Hibernate as the persistence provider).

Can we use @transactional in controller?

No. Both in Java EE and Spring, the current transaction can be programatically marked as "rollback only". So, you can handle an exception and produce a "normal" result, while ensuring the transaction gets rolled back from a @Transactional controller.


Video Answer


2 Answers

The problem with your code is that you expect a transaction to be started when you call saveTaskResult(). This won't happen because Spring uses AOP to start and stop transactions.

If you get an instance of a transactional Spring bean from the bean factory, or through dependency injection, what you get is in fact a proxy around the bean. This proxy starts a transaction before calling the actual method, and commits or rollbacks the transaction once the method has completed.

In this case, you call a local method of the bean, without going through the transactional proxy. Put the saveTaskResult() method (annotated with @Transactional) in another Spring bean. Inject this other Spring bean into DemoService, and call the other Spring bean from the DemoService, and everything will be fine.

like image 172
JB Nizet Avatar answered Sep 30 '22 04:09

JB Nizet


Transactions are held at thread local storage.
If your other method is running a thread with @Transactional annotation.
The default is set to REQUIRED and this means that if you run a method annotated with @Transacitonal from a different thread, you will have a new transaction (as there is no transaction held in the thread local storage of this thread).

like image 35
Yair Zaslavsky Avatar answered Sep 30 '22 05:09

Yair Zaslavsky