当前位置: 首页 > 知识库问答 >
问题:

Spring Batch项目读取器只执行一次

谢鸿羲
2023-03-14

试图实现Spring批处理,但面临一个奇怪的问题,我们的ItemReader类只执行了一次。

以下是细节。

  1. 如果我们在DB中有1000行。
  2. 我们的项目阅读器从DB中获取1000行,并将列表传递给ItemWriter
  3. ItemWriter成功删除所有项目。
  4. 现在ItemReader再次尝试从数据库中获取数据,但没有找到,因此返回NULL,因此执行停止。
  5. 但是我们已经将批处理配置为使用Quartz调度器执行,这是每分钟执行一次。
  6. 现在,如果我们通过转储导入在DB中插入让我们说1000行,批处理作业应该在下一次执行中选择这个数据,但是它甚至没有执行,尽管JobLauncher正在执行。

配置:-

1.我们有ItemReader和ItemWriter,提交间隔等于1。

<batch:job id="csrfTokenBatchJob">
    <batch:step id="step1">
      <tasklet>
        <chunk reader="csrfTokenReader" writer="csrfTokenWriter" commit-interval="1"></chunk>
      </tasklet>
    </batch:step>
  </batch:job>

2.作业被安排在每一分钟被触发。

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
      <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
        <property name="jobDetail" ref="jobDetail" />
        <property name="cronExpression" value="0 0/1 * * * ?" />
      </bean>
    </property>
  </bean>

3.职务配置

<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="com.tavant.oauth.batch.job.CSRFTokenJobLauncher" />
    <property name="jobDataAsMap">
        <map>
            <entry key="jobName" value="csrfTokenCleanUpBatchJob" />
            <entry key="jobLocator" value-ref="jobRegistry" />
            <entry key="jobLauncher" value-ref="jobLauncher" />
        </map>
    </property>
</bean>

第一次执行成功,但后来没有执行,但我可以在日志中看到JobLauncher正在执行。

@Component("csrfTokenReader")
@Scope(value="step")
public class CSRFTokenReader implements ItemReader<List<CSRFToken>> {

    private static final Logger logger = LoggerFactory.getLogger(CSRFTokenReader.class);

    @Autowired
    private CleanService cleanService;

    @Override
    public List<CSRFToken> read() {
        List<CSRFToken> csrfTokenList = null;
        try{

            int keepUpto = Integer.valueOf(PropertiesContext.getInstance().getProperties().getProperty("token.keep", "1"));

            Calendar calTime = Calendar.getInstance();
            calTime.add(Calendar.HOUR, -keepUpto);
            Date toKeep = calTime.getTime();

            csrfTokenList = cleanService.getCSRFTokenByTime(toKeep);
        }
        catch(Throwable th){
            logger.error("Exception in running job At " + new Date() + th);
        }
        if(CollectionUtils.isEmpty(csrfTokenList)){
            return null;
        }
        return csrfTokenList;
    }
}

编辑:--

public class CSRFTokenJobLauncher extends QuartzJobBean {
    static final String JOB_NAME = "jobName";
    private JobLocator jobLocator;
    private JobLauncher jobLauncher;
    public void setJobLocator(JobLocator jobLocator) {
        this.jobLocator = jobLocator;
    }
    public void setJobLauncher(JobLauncher jobLauncher) {
        this.jobLauncher = jobLauncher;
    }
    @Override
    protected void executeInternal(JobExecutionContext context) {
        Map<String, Object> jobDataMap = context.getMergedJobDataMap();
        String jobName = (String) jobDataMap.get(JOB_NAME);
        log.info("Quartz trigger firing with Spring Batch jobName="+jobName);
        JobParameters jobParameters = getJobParametersFromJobMap(jobDataMap);
        try {
            jobLauncher.run(jobLocator.getJob(jobName), jobParameters);
        }
        catch (JobExecutionException e) {
            log.error("Could not execute job.", e);
        }
    }
    private JobParameters getJobParametersFromJobMap(Map<String, Object> jobDataMap) {
        JobParametersBuilder builder = new JobParametersBuilder();
        for (Entry<String, Object> entry : jobDataMap.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if (value instanceof String && !key.equals(JOB_NAME)) {
                builder.addString(key, (String) value);
            }
            else if (value instanceof Float || value instanceof Double) {
                builder.addDouble(key, ((Number) value).doubleValue());
            }
            else if (value instanceof Integer || value instanceof Long) {
                builder.addLong(key, ((Number)value).longValue());
            }
            else if (value instanceof Date) {
                builder.addDate(key, (Date) value);
            }
        }
        return builder.toJobParameters();
    }
}

共有2个答案

关玄裳
2023-03-14

Spring batch在数据库中记录每个作业的执行情况。这就是为什么spring batch需要区分每个作业运行。它检查作业是否已在同一天执行,并且不会再次启动,除非任何作业参数与上一次运行不同,或者如果启用了完整设置,则允许启动。

选项1:-如上所述,我们可以使用允许-启动-如果-完成="true"

选项2:-总是传递一个作业参数,它是当前日期时间戳。这样作业参数值总是唯一的。

    JobExecution jobExecution = jobLauncher.run(reportJob, new JobParametersBuilder()
                    .addDate("now", new Date()).build());

选项3:-使用增量管理器,例如RunIdCrementer,这样我们就不需要确保每次都传递唯一的作业参数。

    @Bean
    public Job job1(JobBuilderFactory jobs, Step s1) {
        return jobs.get("job1")
                .incrementer(new RunIdIncrementer())
                .flow(s1)
                .end()
                .build();
    }
尉迟哲瀚
2023-03-14

经过几个小时的时间浪费,这个问题似乎已经解决了,我已经在tasklet中配置了允许-启动-如果-完成="true"。现在批处理项目阅读器正在按照计划执行。

<batch:job id="csrfTokenBatchJob">
    <batch:step id="step1">
      <batch:tasklet allow-start-if-complete="true">
        <batch:chunk reader="csrfTokenReader" writer="csrfTokenWriter" commit-interval="1"></batch:chunk>
      </batch:tasklet>
    </batch:step>
  </batch:job>
 类似资料:
  • 我们的SpringBatch作业只有一个步骤,包括ItemReader、ItemProcessor和ItemWriter。我们用不同的参数同时运行同一个作业。ItemReader是有状态的,因为它包含从中读取的输入流。 因此,我们不希望ItemReader的相同实例用于每个JobInstance(作业参数)调用。 我不太确定哪种是这种情况的最佳“范围界定”。 1) 该步骤是否应该以@JobScop

  • 我有一个springbatch应用程序,我将连接到一个网络数据库进行阅读。我发现的大多数示例都嵌入了数据库,用于独立测试。我不想那样做。我不确定配置是否类似。以下是我所拥有的最具德国特色的部分(带有一些异议): ,然后从应用程序启动: 我没有配置数据源。我不确定,因为我的基本上只是一个到远程网络数据库的客户端连接,它的配置是否会与我找到的各种示例的嵌入式设置不同。所以我自然会出错(如下)。我只是在

  • 问题内容: 我正在尝试使用jaxb- maven插件使用JAXB将多个XSD转换为不同包中的POJO。我已将其设置为使用多个执行块,然后执行第一个执行块,然后出现一条消息:模式或绑定文件中未检测到更改 这是我pom.xml的摘录: 这是我收到的错误消息: 如果我交换执行块,则第一个总是执行,其余两个块得到相同的消息。 关于如何解决这个问题的任何想法? 问题答案: 通过升级到版本1.6进行了修复 和

  • 我有一个带有Spring执行器的简单项目,还有一个生成git.properties(驻留在classes目录中)的maven插件。 然而,当我运行我的应用程序时,/acturet/info request显示: 文档说明autoconfigure应该自动获取属性https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/

  • 项目Reactor3.1.5。发布 考虑这一点: 我希望订阅服务器在多个线程中运行,但它只在一个线程中运行: 留档告诉我的期望是正确的(http://projectreactor.io/docs/core/release/reference/#threading)。有人能给我解释一下那里发生了什么吗?

  • 本文向大家介绍解决vue项目中type=”file“ change事件只执行一次的问题,包括了解决vue项目中type=”file“ change事件只执行一次的问题的使用技巧和注意事项,需要的朋友参考一下 问题描述 在最近的项目开发中遇到了这样的一个问题,当我上传了一个文件时,我将获取到的文件名清空后,却无法再次上传相同的文件 因为我只是将data中的属性值清空而已,文件名没有变当然会不出发ch