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);
}
}
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.
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.
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).
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.
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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With