springbatch获取需要执行的所有step spring batch 读取文件_数据库


SpringBatch从入门到放弃001- HelloWorld

经常有人问我,有没有一个 Spring Batch 的例子可以参考一下,之前的我的回答一般是百度自己找,太多了。但是后来我发现因为 Spring Batch的版本太稳定,网上的例子大部分都是基于2.X,3.X版本的,还有就是对应的 Spring Boot 的版本也是比较老的。针对这种情况,我决定根据最新 release(4.1.2)的官方文档,来写一套最新的例子,供朋友参考。

Spring Boot 集成了 Spring Batch,如果想在程序钟启用 Spring Batch 的话,只需要添加@EnableBatchProcessing 注解即可,Spring Boot 会根据内置的 @BatchAutoConfiguration引入必要的配置。


springbatch获取需要执行的所有step spring batch 读取文件_数据库_02

Spring Boot 官方文档中关于 Spring Batch 的描述


下边我们就来基于 Spring Boot 搭建一个 Hello World 的 Spring Batch。我先假想一个简单的需求:读取某一个目录的文件,加工一下,再写入另外一个文件(原谅一个理工科直男只能想到这么一个傻逼需求)。

Step 1: 新建一个 Spring Boot 的工程

我们通过 IDEA新建一个 Spring Boot的工程,在新建的时候引入两个必要的依赖:

  1. Spring Batch: 运行 Spring Batch
  2. MySQL Driver: 需要通过Spring Batch 需要通过数据库存储Job 的运行数据


springbatch获取需要执行的所有step spring batch 读取文件_spring_03

新建 Spring Boot 工程


打开 POM 我们会发现其实引入batch 的 starter。再 starter 里面封装了 batch-core。

org.springframework.boot spring-boot-starter-batch mysql mysql-connector-java runtime

既然引入了 MySQL 的依赖,就配置一个数据源吧,打开 application.properties 文件添加数据源配置(Spring Boot 默认的数据库连接池,所以只要指定数据库连接信息就好了),

spring.datasource.username=XXXspring.datasource.password=XXXspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url= jdbc:mysql://localhost:3306/batch_config?zeroDateTimeBehavior=convertToNull

我们需要再程序运行的时候初始化数据库,再application.properties 添加下边两个参数。

#程序启动的时候初始化一个空数据结构spring.batch.initialize-schema=embedded#程序启动的时候不运行任何 Jobspring.batch.job.enabled=false

Step2: 定义Reader/Processor/Writer

根据我们假想出来的需求,我们需要定一个 Reader 来读取我们的文件,Spring Batch 内置了很多很好用的 Reader,后边章节我也会详细的介绍,这里我们选用FlatFileItemReader来读取文件,Spring Batch 提供了FlatFileItemReaderBuilder来帮助我们构建FlatFileItemReader,我们只需要指定几个简单的属性,就可以完成一个读取文件的 Reader。代码如下

@StepScope @Bean public ItemReader itemReader(){ //FlatFileItemReader return new FlatFileItemReaderBuilder() .name("simpleFileReader") .resource(new ClassPathResource("batch-data/2019072401.txt")) .lineMapper(new PassThroughLineMapper()) .build(); }

文件读完之后,我们对读出来的每一行统一添加一个字符串来模拟Batch 的处理过程。我们通过实现一个匿名来完成添加字符串的操作:

@StepScope @Bean public ItemProcessor itemProcessor(){ return new ItemProcessor() { @Override public String process(String item) throws Exception { return "[linghuxiong]"+item; } }; }

处理完读出来的内容之后我们就需要将读出来的字符串写到数据库或者文件系统里面了,我们假想的需求是写文件。同样的 Spring Batch 官方提供了很多内置的 Writer。因为是写文件,我们选用FlatFileItemWriter作为我们的 Writer,同样的我们使用FlatFileItemWriterBuilder来构建我们的FlatFileItemWriter[Spring Batch 4 对FlatFileItemWriterBuilder 进行了增强,详见官方文档]。

@StepScope @Bean public ItemWriter itemWriter(){ return new FlatFileItemWriterBuilder() .name("simpleFileWriter") .lineAggregator(new PassThroughLineAggregator()) .resource(new FileSystemResource("/Users/eric/Documents/dev/linghuxiong/spring-boot-demo/batch/target/2019072402.txt")) .build(); }

至此我们的Reader/Processor/Writer 就已经构建完毕了,这就相当于零件已经备好,下边我们通过构建 Step/Job 来组装我们的 Batch。

Step 3: 构建 Step /Job

再给项目添加@EnableBatchProcessing之后,就会默认将 Batch 需要对象注入到 Context 中,其中就有构建 Step 的StepBuilderFactory和构建 Job 的JobBuilderFactory。下边我们就用这两个 Factory 来构建我们的 Step 和 Job。

首先注入这两个 Factory :

@Autowired JobBuilderFactory jobBuilderFactory; @Autowired StepBuilderFactory stepBuilderFactory;

将 Step2 中的零件组装成一个 Step:

@Bean public Step step1(){ return stepBuilderFactory.get("step1") .chunk(2) .reader(itemReader()) .processor(itemProcessor()) .writer(itemWriter()) .build(); }

将 Step 放入 Job 中:

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

经过以上两步,我们的 Job 就定义完成了。下边我们就来测试一下我们写的 Job 是否正确:

Step 4: 测试一个 Job

Spring Batch 4.X 提供了一个 @SpringBatchTest 会帮助我们构建一个 Batch 的执行环境,比如:JobLauncherTestUtils/JobRepositoryTestUtils 两个类。

@RunWith(SpringRunner.class)@SpringBatchTest@SpringBootTestpublic class JobTest { @Autowired private JobLauncherTestUtils jobLauncherTestUtils; @Autowired private JobRepositoryTestUtils jobRepositoryTestUtils;  … … … …}

在测试我们的Job 之前,我们需要先清空我们的测试库,这样就不会因为上一次的失败,而引入的脏数据来影响我们这一次的测试。

@Before public void clearMetadata() { jobRepositoryTestUtils.removeJobExecutions(); }

编写一个简单的测试 CASE,运行我们的 JOB,并判单是否正常结束:

@Test public void testJob() throws Exception { // given JobParameters jobParameters = jobLauncherTestUtils.getUniqueJobParameters(); // when JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobParameters); // then Assert.assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus()); }

好了,截止到先,我们的 Hello World 的所有代码都已经编写结束了。整体的项目结构如下:


springbatch获取需要执行的所有step spring batch 读取文件_springbatch读取文件_04

项目结构


运行我们Junit Case , 运行成功之后就会在 Writer 指定的文件目录下边生成我们处理之后的文件。


springbatch获取需要执行的所有step spring batch 读取文件_spring_05

运行效果


同时在我们配置的数据库中,会初始化Batch 框架自己所需要的表:


springbatch获取需要执行的所有step spring batch 读取文件_mysql_06

数据库创建的表