Java 手动更改 Spring Batch 元数据表是一个坏主意吗?

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

Is it a bad idea to change the Spring Batch Meta-Data tables manually?

javaspring-batch

提问by songyuanyao

Background

背景

I'm using Spring Batch 2.1.8, and run jobs by CommandLineJobRunner. Such as:

我正在使用 Spring Batch 2.1.8,并通过CommandLineJobRunner. 如:

java org.springframework.batch.core.launch.support.CommandLineJobRunner classpath:launchContext.xml theJobId

Problem

问题

At some condition such as a server crash, running job could be interrupted. But the interrupted job left a STARTEDstatus in the Spring Batch Meta-Data tables, and can't be run again.

在某些情况下,例如服务器崩溃,正在运行的作业可能会中断。但是中断的作业STARTED在 Spring Batch Meta-Data 表中留下了状态,并且无法再次运行。

org.springframework.batch.core.repository.JobExecutionAlreadyRunningException: A job execution for this job is already running

I can think of two solutions:

我可以想到两种解决方案:

Solution1

解决方案1

Add a new job parameter and change it everytime to make it a "new" job for Spring Batch. Such as:

添加一个新的作业参数并每次更改它以使其成为 Spring Batch 的“新”作业。如:

java org.springframework.batch.core.launch.support.CommandLineJobRunner classpath:launchContext.xml theJobId times=0

And when need to rerun it, do clear all corresponding output data, count up timesonce, and then rerun the job.

当需要重新运行时,清除所有相应的输出数据,计数times一次,然后重新运行作业。

Solution2

解决方案2

Change the Spring Batch Meta-Data tables manually.

手动更改 Spring Batch 元数据表。

To update the status to make the job restartable. Such as:

更新状态以使作业可重新启动。如:

UPDATE BATCH_JOB_EXECUTION SET END_TIME = SYSTIMESTAMP, STATUS = 'FAILED', EXIT_CODE = 'FAILOVER' WHERE JOB_EXECUTION_ID =
    (SELECT MAX(JOB_EXECUTION_ID) FROM BATCH_JOB_EXECUTION WHERE JOB_INSTANCE_ID =
        (SELECT MAX(JOB_INSTANCE_ID) FROM BATCH_JOB_INSTANCE WHERE JOB_NAME = 'XXX'));

I've tried it and it seems works well.

我试过了,似乎效果很好。

Question

Is Solution2 a bad idea? Are there any traps?

解决方案 2 是个坏主意吗?有陷阱吗?

Thanks in advance. And any other solutions are appreciated.

提前致谢。任何其他解决方案都值得赞赏。

采纳答案by Michael Minella

Solution 2 is the accepted approach right now. The API does not provide a way to fix this scenario. There have been requests in the past for the framework to clean up automatically, but 99% of the time, a human decision is needed to determine if cleanup is truly required.

解决方案 2 是目前公认的方法。API 不提供修复这种情况的方法。过去曾有人要求框架自动清理,但在 99% 的情况下,需要人工决定来确定是否确实需要清理。

My only note for option 2 would be to check the BATCH_STEP_EXECUTION table as well to see what state the last executed step was left in.

我对选项 2 的唯一说明是还要检查 BATCH_STEP_EXECUTION 表,以查看最后执行的步骤处于什么状态。

回答by Steef

I created a specific spring bean for this which is triggered on a container refresh (which happens on app (re)start too).

我为此创建了一个特定的 spring bean,它在容器刷新时触发(这也发生在应用程序(重新)启动时)。

It searches for 'running' jobs, marks them 'FAILED' and restarts them.

它搜索“正在运行”的作业,将它们标记为“失败”并重新启动它们。

import java.util.Date;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class BatchJobRestarter implements ApplicationListener<ContextRefreshedEvent> {

    private static final Logger LOGGER  = LoggerFactory.getLogger(BatchJobRestarter.class);

    @Autowired
    private JobExplorer         jobExplorer;

    @Autowired
    JobRepository               jobRepository;

    @Autowired
    JobOperator                 jobOperator;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        LOGGER.info("Container restart: restarting 'running' batch jobs");
        List<String> jobs = jobExplorer.getJobNames();
        for (String job : jobs) {
            Set<JobExecution> runningJobs = jobExplorer.findRunningJobExecutions(job);

            for (JobExecution runningJob : runningJobs) {
                try {
                    LOGGER.info("Restarting job {} with parameters {}", runningJob.getJobInstance().getJobName(), runningJob.getJobParameters().toString());
                    runningJob.setStatus(BatchStatus.FAILED);
                    runningJob.setEndTime(new Date());
                    jobRepository.update(runningJob);
                    jobOperator.restart(runningJob.getId());
                } catch (Exception e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
        }
    }
}

Steef

斯蒂夫