Batch(二)-- Job的使用

前言

在上一篇文章中,我们主要介绍了batch的各个组件的作用,并且写了一个简单的demo。在这一篇中,我们主要学习一下Job的使用。

Job的创建

从上一篇中我们可以看到job是由JobBuilderFactory 创建。

step

我们知道一个job可以由一个或多个step组成,上一篇中我们只写了一个step,这里我们多加几个step。

多个step

这里我们在上一篇demo的基础上再写一个2个step:

@Configuration
@EnableBatchProcessing
public class BatchConfiguration {
    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job demoJob1() {
    	// 通过JobBuilderFactory 创建
        return jobBuilderFactory.get("demoJob1")
                .start(demoStep1())
                .next(demoStep2())
                .next(demoStep3())
                .build();
    }

    @Bean
    public Step demoStep1() {
    	// 通过StepBuilderFactory 创建
        return stepBuilderFactory.get("demoStep1").tasklet(new Tasklet() {
            @Override
            public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
                System.out.println("======= demoStep1 ======");
                return RepeatStatus.FINISHED;
            }
        }).build();
    }

    @Bean
    public Step demoStep2() {
        return stepBuilderFactory.get("demoStep2").tasklet(new Tasklet() {
            @Override
            public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
                System.out.println("======= demoStep2 ======");
                return RepeatStatus.FINISHED;
            }
        }).build();
    }

    @Bean
    public Step demoStep3() {
        return stepBuilderFactory.get("demoStep3").tasklet(new Tasklet() {
            @Override
            public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
                System.out.println("======= demoStep3 ======");
                return RepeatStatus.FINISHED;
            }
        }).build();
    }
}
第一次启动项目运行:

在这里插入图片描述
可以看到之前运行过的step1不会再执行,执行了step2和step3。
这是因为,Spring Batch中相同的Job,当所带参数一致的时候,有且只会启动一次。
我们看看数据库中:
job:
在这里插入图片描述
step:
在这里插入图片描述

第二次启动执行:

查看日志:
在这里插入图片描述
数据库中:
job:
在这里插入图片描述
step:
在这里插入图片描述
从数据库中我们可以看到step的status状态都是complete即完成的状态。
但是往往在业务中,我们可以通过判断step的状态来决定下一步我们需要做什么。

step的状态

我们修改job调用的配置,使它变成有状态判定的调用:

@Bean
public Job demoJob1() {
    return jobBuilderFactory.get("demoJob1")
            .start(demoStep1())
            // .next(demoStep2())
            // .next(demoStep3())
            .on("COMPLETED").to(demoStep4())
            .from(demoStep4()).on("COMPLETED").to(demoStep5())
            .from(demoStep5()).end()
            .build();
}

@Bean
public Step demoStep4() {
    return stepBuilderFactory.get("demoStep4").tasklet(new Tasklet() {
        @Override
        public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
            System.out.println("======= demoStep4 ======");
            return RepeatStatus.FINISHED;
        }
    }).build();
}
@Bean
public Step demoStep5() {
    return stepBuilderFactory.get("demoStep5").tasklet(new Tasklet() {
        @Override
        public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
            System.out.println("======= demoStep5 ======");
            return RepeatStatus.FINISHED;
        }
    }).build();
}

只有在step1返回的状态时COMPLETED时,才会往下调用。
日志:
在这里插入图片描述

Step的内部 – Tasklet和Chunk

上面的例子我们的是通过创建实现Tasklet的对象,并重写其中的excute方法来完成的step的执行

return stepBuilderFactory.get("demoStep2").tasklet(new Tasklet() {
   @Override
    public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
        System.out.println("======= demoStep2 ======");
        return RepeatStatus.FINISHED;
    }
}).build();

这里我们使用另一种方式,通过chunk来完成step的执行:

@Bean
public Step demoStep17() {
    AtomicInteger i = new AtomicInteger(0);
    // chunk(2)执行2次
    return stepBuilderFactory.get("demoStep15").chunk(2).reader(new ItemReader<Object>() {
        @Override
        public Object read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
            System.out.println("===========demoStep17 reader ============");
            i.addAndGet(1);
            if (i.get() > 3)
                return null;
            return "read";
        }
    }).processor(new ItemProcessor<Object, Object>() {
        @Override
        public Object process(Object o) throws Exception {
            System.out.println("===========demoStep17 process ============");

            return "process";
        }
    }).writer(new ItemWriter<Object>() {
        @Override
        public void write(List<?> list) throws Exception {
            System.out.println("===========demoStep17 write ============");
        }
    }).build();
}

运行结果:
在这里插入图片描述
第四次执行reader时,返回null,退出了这个step,虽然退出了,但还是把第三次的process和writer执行完了。
具体见:https://blog.csdn.net/neweastsun/article/details/88866837

Job的触发

在上一篇中,我们在配置文件中将项目启动时自动执行job设置为了true,但是更多的时候,我们希望job的触发是被我们控制的。这里便用到了jobLauncher执行器。

#是否启动项目时候,自动执行job
#spring.batch.job.enabled = false
JobLauncher

通过JobLauncher,我们可以通过调用api的方式,来启动我们指定的job。
(当然我们也可以用定时器触发,这里我们先不讲,后面结合quartz使用时我们再说)
首先定义job和step

@Bean
public Job demoJob2() {
    return jobBuilderFactory.get("demoJob2")
            .start(demoStep10()).build();
}

@Bean
public Step demoStep10() {
    return stepBuilderFactory.get("demoStep10").tasklet(new Tasklet() {
        @Override
        public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
            System.out.println("======= demoStep10 ======");
            return RepeatStatus.FINISHED;
        }
    }).build();
}

定义api接口调用

@RestController
public class JobController1 {
    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private Job demoJob2;

    @GetMapping("/launcherJob")
    public String runJob1(String param) throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
        System.out.println("========== Job Launcher run with param :" + param);
        JobParameters jobParameters = new JobParametersBuilder().addString("param", param).toJobParameters();
        jobLauncher.run(demoJob2, jobParameters);

        return "========== Run Job2 Success ==========";
    }
}

客户端调用:
在这里插入图片描述
控制台输出:
在这里插入图片描述
查看数据库的batch_job_execution_params表可以看到KEY_NAME和STRING_VAL字段有我们传入的值

quartz + JobLauncher

我们可以通过quartz,在quartz的job中通过JobLauncher触发batch的job:

public abstract class TuiJob extends QuartzJobBean {


    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private JobLocator jobLocator;

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis())
                .toJobParameters();
        try {
            Job job = jobLocator.getJob(getJobName());
            JobExecution jobExecution = jobLauncher.run(job, jobParameters);
            jobExecution.getStatus();
        } catch (NoSuchJobException | JobExecutionAlreadyRunningException | JobRestartException
                | JobInstanceAlreadyCompleteException | JobParametersInvalidException e) {
            e.printStackTrace();
        }
    }
	
	// 通过实现该方法,定制化启动不同的batch job
    protected abstract String getJobName();
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值