Java 已在事务中时启动弹簧批处理作业

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

Start a spring batch job when already within a transaction

javaspringtransactionsspring-batch

提问by Hyman

I have simple Spring-Service that (among other tasks) starts a spring batch job with the following code:

我有一个简单的 Spring-Service,它(在其他任务中)使用以下代码启动一个 spring 批处理作业:

@Autowired
private JobRegistry jobRegistry;

@Autowired
private JobLauncher jobLauncher;

public void startMyJob() {
    Job job = jobRegistry.getJob("myJobName");
    JobParameters jobParameters = new JobParametersBuilder().toJobParameters();
    jobLauncher.run(job, jobParameters);
}

This works fine, as long as there is no transaction active when the Serivce-Method is called. However, with an active transaction, I get this exception:

只要在调用 Serivce-Method 时没有活动的事务,就可以正常工作。但是,对于活动事务,我收到此异常:

Caused by: java.lang.IllegalStateException: Existing transaction detected in JobRepository. Please fix this and try again (e.g. remove @Transactional annotations from client).

I cannot easily remove the existing transaction, since it is implied due to some framework code that is not within my reach.

我无法轻松删除现有事务,因为它是由于某些框架代码无法实现而隐含的。

So, how can I start the job anyway within this context? The new job just should not use the existing transaction. It could just start its own transaction - but how to configure it to make it work?

那么,在这种情况下,我怎样才能开始工作呢?新作业不应使用现有事务。它可以只开始自己的事务——但如何配置它以使其工作?

采纳答案by Luca Basso Ricci

Use AbstractJobRepositoryFactoryBean.ValidateTransactionState, but use carefully (Warning: Dragons ahead).

使用AbstractJobRepositoryFactoryBean.ValidateTransactionState,但要小心使用(警告:前方有龙)。

To use another transaction you can inject a custom SimpleJobLauncher.executorwith method Executor.runmarked as @Transactional() (or create a custom JobLauncherand do the same trick on method run).

要使用另一个事务,您可以SimpleJobLauncher.executor使用Executor.run标记为 @Transactional() 的方法注入自定义(或创建自定义JobLauncher并在 method 上执行相同的技巧run)。

I haven't tried because I haven't faced the problem, but hope can help.

我没有尝试过,因为我还没有遇到过这个问题,但希望能有所帮助。

回答by Luca Basso Ricci

I've dealt with this by creating a separate bean AsyncJobLauncher which has a run method with the same signature as JobLauncher and delegates to the real one, but marked with Spring's @Async. So, the launching of the job happened in the background on a new thread, so it was in its own transaction.

我通过创建一个单独的 bean AsyncJobLauncher 来处理这个问题,它有一个与 JobLauncher 具有相同签名的 run 方法,并委托给真正的方法,但用 Spring 的 @Async 标记。因此,作业的启动发生在新线程的后台,因此它在自己的事务中。

回答by Schaka

I had a similar problem. In particular, I was starting my jobs from within a class that was annotated with @Transactional(propagation = Propagation.REQUIRES_NEW). To work around this, I started my jobs from a somewhere, where I had no active transaction. Then I ran into your problem as described above - and here is how I solved it:

我有一个类似的问题。特别是,我是从一个用@Transactional(propagation = Propagation.REQUIRES_NEW). 为了解决这个问题,我从一个没有活动事务的地方开始我的工作。然后我遇到了你的问题,如上所述 - 这是我解决它的方法:

set validateTransactionStateto false on the JobRepository - it now ignored the transactions that were automatically created in the class my ItemProcessorcalled (because said class was annotated with @Transcational, as explained above).

validateTransactionState在 JobRepository 上设置为 false - 它现在忽略在我ItemProcessor调用的类中自动创建的事务(因为所述类用@Transcational 进行了注释,如上所述)。

This made my jobs run just fine. However, I wasn't sure if I was now dealing with 2 open transactions and wanted to be sure I was not. So I also removed the transcational annnotation on the class level and moved it to all public methods, except the ones called by my Itemprocessor. Thus I solved the issue.

这使我的工作运行得很好。但是,我不确定我现在是否正在处理 2 个未结交易,并想确定我不是。所以我也去掉了类级别的transcational注解,把它移到所有公共方法中,除了我的Itemprocessor调用的方法。因此我解决了这个问题。

回答by Corneil du Plessis

My solution was that the method starting the job needed to be annotated with @Transactional(propagation = Propagation.NOT_SUPPORTED)this would suspend the current transaction and resume after execution.

我的解决方案是,启动作业的方法需要用@Transactional(propagation = Propagation.NOT_SUPPORTED)this进行注释,这将挂起当前事务并在执行后恢复。

You can set transactionAttributeon your readers and writers if you need anything different from defaults.

transactionAttribute如果您需要与默认值不同的任何内容,您可以设置您的读者和作者。

I did not need setValidateTransactionState(false)on the JobRepositoryFactoryBeanor JobRepository

我不需要setValidateTransactionState(false)JobRepositoryFactoryBeanJobRepository