java 如何将 Spring 事务传播到另一个线程?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5232351/
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-30 10:06:45  来源:igfitidea点击:

How to propagate Spring transaction to another thread?

javamultithreadingspringtransactionspropagation

提问by dma_k

Perhaps, I am doing something wrong, but I can't find a good way out for the following situation.

也许,我做错了什么,但我找不到以下情况的好方法。

I would like to unit test a service that uses Spring Batchunderneath to execute jobs. The jobs are executed via pre-configured AsyncTaskExecutorin separate threads. In my unit test I would like to:

我想对一个在下面使用Spring Batch来执行作业的服务进行单元测试。作业通过AsyncTaskExecutor在单独的线程中预先配置来执行。在我的单元测试中,我想:

  1. Create few domain objects and persist them via DAO
  2. Invoke the service method to launch the job
  3. Wait until the job is completed
  4. Use DAO to retrieve domain objects and check their state
  1. 创建少量域对象并通过 DAO 持久化它们
  2. 调用服务方法来启动作业
  3. 等待作业完成
  4. 使用 DAO 检索域对象并检查它们的状态

Obviously, all above should be executed within one transaction, but unfortunately, transactions are not propagated to new threads(I understand the rationale behind this).

显然,上述所有内容都应该在一个事务中执行,但不幸的是,事务不会传播到新线程(我理解这背后的基本原理)。

Ideas that came to my mind:

我想到的想法:

  • Commit the transaction#1 after step (1). Is not good, as the DB state should be rolled back after the unit test.
  • Use Isolation.READ_UNCOMMITTEDin job configuration. But this requires two different configurations for test and for production.
  • 在步骤 (1) 之后提交事务 #1。不好,因为数据库状态应该在单元测试后回滚。
  • Isolation.READ_UNCOMMITTED在作业配置中使用。但这需要两种不同的配置用于测试和生产。

采纳答案by gkamal

I think the simplest solution would be configure the JobLauncher with a SyncTaskExecutor during test execution - this way the job is executed in the same thread as the test and shares the transaction.

我认为最简单的解决方案是在测试执行期间使用 SyncTaskExecutor 配置 JobLauncher - 这样作业在与测试相同的线程中执行并共享事务。

The task executor configuration can be moved to a separate spring configuration xml file. Have two versions of it - one with SyncTaskExecutor which is used during testing and the other AsyncTaskExecutor that is used for production runs.

可以将任务执行器配置移动到单独的 spring 配置 xml 文件中。有两个版本——一个是在测试期间使用的 SyncTaskExecutor,另一个是用于生产运行的 AsyncTaskExecutor。

回答by rustyx

Although this is not a true solution to your question, I found it possible to start a newtransaction inside a worker thread manually. In some cases this might be sufficient.

尽管这不是您问题的真正解决方案,但我发现可以在工作线程中手动启动事务。在某些情况下,这可能就足够了。

Source: Spring programmatic transactions.

来源:Spring 程序化事务

Example:

例子:

@PersistenceContext
private EntityManager entityManager;
@Autowired
private PlatformTransactionManager txManager;

/* in a worker thread... */
public void run() {
    TransactionStatus tx = txManager.getTransaction(new DefaultTransactionDefinition());
    try {
        entityManager.find(...)
        ...
        entityManager.flush(...)
        etc...
        txManager.commit(tx);
    } catch (RuntimeException e) {
        txManager.rollback(tx);
    }
}

回答by dfichter

If you do want separate configurations, I'd recommend templating the isolation policy in your configuration and getting its value out of a property file so that you don't wind up with a divergent set of Spring configs for testing and prod.

如果您确实需要单独的配置,我建议您在配置中模板化隔离策略并从属性文件中获取其值,这样您就不会得到一组不同的 Spring 配置来进行测试和生产。

But I agree that using the same policy production uses is best. How vast is your fixture data, and how bad would it be to have a setUp()step that blew away and rebuilt your data (maybe from a snapshot, if it's a lot of data) so that you don't have to rely on rollbacks?

但我同意使用相同的政策生产用途是最好的。您的设备数据有多大,如果有一个setUp()步骤将您的数据吹走并重建您的数据(可能来自快照,如果数据很多)以便您不必依赖回滚,这有多糟糕?