从另一个线程调用@Transactional方法(Runnable)

时间:2021-04-24 20:57:15

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

在新的线程中使用JPA将数据保存到数据库中,有什么简单的解决方案吗?

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.

我的基于Spring的web应用程序允许用户管理计划任务。在运行时,他可以创建和启动预定义任务的新实例。我正在使用spring的TaskScheduler,一切都运行良好。

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.

编辑:我必须概括我的问题:我需要从tasks中调用@Service类上的一个方法。因为任务结果必须在保存到数据库之前进行“处理”。

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.

编辑2:我的问题代码的简化版本来到这里。当从调度程序调用saveTaskResult()时,会打印出消息,但不会保存到db中。但是每当我从controller调用saveTaskResult()时,记录都会被正确地保存到数据库中。

@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);
    }

}

2 个解决方案

#1


15  

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.

代码的问题是,在调用saveTaskResult()时,您希望启动事务。这不会发生,因为Spring使用AOP启动和停止事务。

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.

如果您从bean工厂获得了一个事务Spring bean的实例,或者通过依赖注入,您得到的实际上是一个围绕bean的代理。此代理在调用实际方法之前启动事务,并在方法完成后提交或回滚事务。

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.

在这种情况下,无需通过事务代理就可以调用bean的本地方法。将saveTaskResult()方法(用@Transactional注释)放到另一个Spring bean中。将另一个Spring bean注入DemoService,并从DemoService调用另一个Spring bean,一切都会好起来。

#2


2  

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).

事务在线程本地存储中进行。如果您的其他方法正在运行一个带有@Transactional注释的线程。默认设置为REQUIRED,这意味着如果您从另一个线程运行带有@Transacitonal注解的方法,您将有一个新的事务(因为该线程的线程本地存储中没有事务)。

#1


15  

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.

代码的问题是,在调用saveTaskResult()时,您希望启动事务。这不会发生,因为Spring使用AOP启动和停止事务。

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.

如果您从bean工厂获得了一个事务Spring bean的实例,或者通过依赖注入,您得到的实际上是一个围绕bean的代理。此代理在调用实际方法之前启动事务,并在方法完成后提交或回滚事务。

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.

在这种情况下,无需通过事务代理就可以调用bean的本地方法。将saveTaskResult()方法(用@Transactional注释)放到另一个Spring bean中。将另一个Spring bean注入DemoService,并从DemoService调用另一个Spring bean,一切都会好起来。

#2


2  

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).

事务在线程本地存储中进行。如果您的其他方法正在运行一个带有@Transactional注释的线程。默认设置为REQUIRED,这意味着如果您从另一个线程运行带有@Transacitonal注解的方法,您将有一个新的事务(因为该线程的线程本地存储中没有事务)。