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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11275471/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 04:29:52  来源:igfitidea点击:

Calling @Transactional methods from another thread (Runnable)

javaspringrunnable

提问by Ond?ej Míchal

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.

编辑:我必须概括我的问题:我需要从任务中调用@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 中。但是每当我从控制器调用 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);
    }

}

回答by JB Nizet

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,一切都会好起来的。

回答by Yair Zaslavsky

Transactions are held at thread local storage.
If your other method is running a thread with @Transactionalannotation.
The default is set to REQUIREDand this means that if you run a method annotated with @Transacitonalfrom 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从不同的线程运行注释的方法,您将拥有一个新事务(因为该线程的线程本地存储中没有事务)。