springbatch
一.springbatch概述
Spring Batch 是一个轻量级的、完善的批处理框架,旨在帮助企业建立健壮、高效的批处理应用。Spring Batch是Spring的一个子项目,使用Java语言并基于Spring框架为基础开发,使得已经使用 Spring 框架的开发者或者企业更容易访问和利用企业服务。
Spring Batch 提供了大量可重用的组件,包括了日志、追踪、事务、任务作业统计、任务重启、跳过、重复、资源管理。对于大数据量和高性能的批处理任务,Spring Batch 同样提供了高级功能和特性来支持,比如分区功能、远程功能。总之,通过 Spring Batch 能够支持简单的、复杂的和大数据量的批处理作业。
特点
- 事务管理,让您专注于业务处理,实现批处理机制,你可以引入平台事务机制或其他事务管理器机制
- 基于块Chunk的处理,通过将一大段大量数据分成一段段小数据来处理,。
- 启动/停止/重新启动/跳过/重试功能,以处理过程的非交互式管理。
- 基于Web的管理界面(Spring Batch Admin),它提供了一个用于管理任务的API。
- 基于Spring框架,因此它包括所有配置选项,包括依赖注入。
- 符合JSR 352:Java平台的批处理应用程序。
- 基于数据库管理的批处理,可与Spring Cloud Task结合,适合分布式集群下处理。
- 能够进行多线程并行处理,分布式系统下并行处理,变成一种弹性Job分布式处理框架。
1.springbatch的原理
Spring批处理的基本单元是Job,你需要定义一个Job代表一次批处理工作,每个Job分很多步骤step,每个步骤里面有两种处理方式Tasklet(可重复执行的小任务)和Chunk(块),掌握Spring Batch主要是将这几个核心概念搞清楚。
在SpringBoot架构下,我们只要做一个JobConfig组件作为JobLauncher,使用@Configuration配置,然后完成上图中Job和Step以及ItemReader,ItemProcessor和ItemWriter,后面这三个分别是存在一个步骤里,用于处理条目的输入读 、处理然后输出写出。至于图中JobRepository只要我们在Application.properties中配置上datasource,SpringBoot启动时会自动将batch需要的库表导入到数据库中。
2.springbatch的对象
- JobLauncher:是任务启动器,通过它来启动任务,可以看做是程序的入口。
- Job代表着一个具体的任务。
- Step代表着一个具体的步骤,一个Job可以包含多个Step.在实际业务场景中,可能一个任务很复杂,这个时候可以将任务拆分成多个step,分别对这些step 进行管理(将一个复杂任务简单化)。(这些step 默认是串行执行,也可以并行执行,根据具体的业务场景来使用)。
- itemReader代表着数据的读取,springbatch提供了很多中数据的读取方式,包括读取文件的额数据,读取数据库的数据,多种方式读取数据.
- itemProcesser表示的是处理我们reader读取到的数据,我们通常业务中需要对我们读取到的数据做一些筛选处理,而这些处理就是在processer中.
- itemWriter表示的将processer中处理的数据写入哪里,springbatch同样提供了多种写入方式,包括写入文件,写入数据库,写入多种途径等.
- jobRepository表示springbatch的数据库持久化对象,springbatch会生成多张数据库表,这些表用来记录我们的job等执行情况,而与这些数据库表交互的对象就是jobRepository对象.
- XXXListener 表示监听,springbatch提供了多种监听,包括监听job,step,reader,processer,writer,chunk,skip等.基本上涵盖了我们一个job执行的所有的流程和可能出现的问题.
二.springbatch的场景
编写代码的步骤:
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<!--加入一个内存数据库H2是项目不报错
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
-->
<!--配置mysql数据库-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
配置application.yml
#配置数据源dataSource
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springbatch2?serverTimezone=GMT%2B8&characterEncoding=utf8
username: root
password: root
#这是指定springbatch生成数据库表的sql语句位置
schema: classpath:/org/springframework/batch/core/schema-mysql.sql
batch:
initialize-schema: always #是否生成数据库表,这是代表生成数据库表
job:
#names: SkipExceptionJob7 指定执行的job,这里指定的job的名字,也就是get()参数,而不是beanname
enabled: false #这里是项目启动时不运行任何job的意思.
server:
port: 8001
接下来的步骤就是结合具体的场景,不同实现不同的代码.
1.场景1-demo演示
demo演示了springbatch的job是如何创建的,step等.一个batch的job必须包含至少一个step,demo演示了如何构建job,但是其中还有很多需要注意的地方:
package com.gl.basicjob;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.core.step.tasklet.TaskletStep;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author gl
* @time 2020-04-28 22:10
* @function : 测试多step的job的创建,并且如何指定step的执行顺序
* @step :
*/
@Configuration
@EnableBatchProcessing
public class JobDemo2 {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Job getJobDemo1(){
//第一种指定job中step顺序
/*Job jobDemo1 = jobBuilderFactory.get("jobDemo1") //job的名字
.start(step1()) //这个是指定有三个step的job,顺序分别是step1->step2->step3
.next(step2())
.next(step3())
.build();*/
//第二种指定job中的step,并且指定step的执行顺序
Job job = jobBuilderFactory.get("jobDemo1")
.start(step1())
.on("COMPLETED").to(step2()) //to代表的是到哪个节点step
.from(step2()).on("COMPLETED").to(step3()) //on代表条件,如果执行完成
.from(step3()).end()
.build();
return job;
}
@Bean(value = "job2step3")
public Step step3() {
TaskletStep step = stepBuilderFactory.get("step3")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
System.out.println("step3()");
return RepeatStatus.FINISHED;
}
}).build();
return step;
}
@Bean(value = "job2step2")
public Step step2() {
TaskletStep step = stepBuilderFactory.get("step2")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
System.out.println("step2()");
return RepeatStatus.FINISHED;
}
}).build();
return step;
}
@Bean(value = "job2step1")
public Step step1() {
TaskletStep step = stepBuilderFactory.get("step1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
System.out.println("step1()");
return RepeatStatus.FINISHED;
}
}).build();
return step;
}
}
说明:
1.jobBuilderFactory和jobBuilderFactory对象是spring框架默认就自动帮我注入到spring容器中的,其实还有几个对象例如JobLauncher对象,jobRepository对象等.
2.job可以包含多个step,并且可以指定step的执行顺序和执行条件
3.job也可指定监听,step同样也可指定监听.
4.job除了可以指定step还可以指定flow对象,并且job可以并发执行flow.
2.场景2:数据库db->文件(file)
代码步骤:
(1)配置数据库表的entity对象
/**
* @author gl
* @time 2020-04-29 16:55
* @function :
* @step :
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Customer {
private Long id;
private String firstName;
private String lastName;
private Date birthday;
}
(2)书写reader和writer的配置类
/**
* @author gl
* @time 2020-04-30 15:55
* @function : 这是数据库读取,写入文件的配置信息类,
* 主要配置的从数据库读取的reader对象和写入文件的writer对象
* @step :
*/
@Configuration
public class DbReaderAndWriterConfig {
@Autowired
private DataSource dataSource;
//读取数据库数据,从哪个数据库读取那张表的那些字段信息
@Bean(value = "itemReaderDbCustmer")
@StepScope//一个step,一个实例
public JdbcPagingItemReader<Customer> itemReaderDbCustmer() {
JdbcPagingItemReader<Customer> reader = new JdbcPagingItemReader();
reader.setDataSource(dataSource);//数据源
reader.setFetchSize(2);//分页,每页读取数据
reader.setRowMapper(new RowMapper<Customer>() {
@Override
public Customer mapRow(ResultSet rs, int i) throws SQLException {
Customer customer = new Customer();
customer.setId(rs.getLong(1));
customer.setFirstName(rs.getString(2));
customer.setLastName(rs.getString(3));
customer.setBirthday(rs.getDate(4));
return customer;
}
});
//指定sql语句
MySqlPagingQueryProvider provider = new MySqlPagingQueryProvider();
provider.setSelectClause("id,first_name,last_name,birthday");//指定读取的列名字
provider.setFromClause("from custmer"); //指定表名
//根据那个字段进行排序
Map<String,Order> sort = new HashMap<>(1);
sort.put("id",Order.DESCENDING);
provider.setSortKeys(sort);
//将provider赋值给reader
reader.setQueryProvider(provider);
return reader;
}
//定义输出Writer,写入文件,包括写入的格式是什么
@Bean(value = "itemWriterDbCustmer")
@StepScope
public FlatFileItemWriter<Customer> itemWriterDbCustmer() throws Exception {
FlatFileItemWriter<Customer> writer = new FlatFileItemWriter<>();
writer.setEncoding("utf8");
//classpathResource是当前类路径
//filepathResource是磁盘路劲
writer.setResource(new FileSystemResource("E:\\custmer.txt"));
//把custmer转化成string
writer.setLineAggregator(new LineAggregator<Customer>() {
@Override
public String aggregate(Customer customer) {
//转化成json的字符串
ObjectMapper mapper = new ObjectMapper();
String result = "";
try {
result = mapper.writeValueAsString(customer);
} catch (JsonProcessingException e) {
throw new RuntimeException("对象转换出现异常");
}
return result;
}
});
writer.afterPropertiesSet();
return writer;
}
}
(3)自定义processer类
processer可以不用,因为step中必须要有reader和writer,可以没有processer
/**
* @author gl
* @time 2020-04-30 17:33
* @function : 过滤规则,将读取到的数据经过处理在返回给writer对象
* @step :
*/
@Component("dbToFileProcesser")
//第一个custmer是reader读取到的值,第二个是custmer是处理之后的对象类型
public class dbToFileProcesser implements ItemProcessor<Customer,Customer>{
@Override
public Customer process(Customer customer) throws Exception {
Customer result = new Customer();
result.setId(customer.getId() + 100);
result.setFirstName(customer.getFirstName());
result.setLastName(customer.getLastName());
return result;
}
}
(4)自定义job的配置
/**
* @author gl
* @time 2020-04-30 15:47
* @function : 将数据库读取出来的数据,写入文件中
* @step :
*/
@Configuration
public class ItemWriterFileConfiguration {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
@Qualifier("itemReaderDbCustmer")//注入读取custmer表的reader对象
private ItemReader<Customer> reader;
@Autowired
@Qualifier("itemWriterDbCustmer")
private ItemWriter<Customer> writer;
@Autowired
@Qualifier("dbToFileProcesser")
private ItemProcessor<? super Customer, ? extends Customer> processer;
//这是调用有processer的step的job对象
@Bean(value = "ItemWriterProcesserFileJob")
public Job ItemWriterProcesserFileJob(){
return jobBuilderFactory.get("ItemWriterProcesserFileJob10")//只要job的名字不同,则就可以启动job
.start(ItemWriterProcesserFileStep())
.build();
}
//这是有processer的step步骤
@Bean(value = "ItemWriterProcesserFileStep")
public Step ItemWriterProcesserFileStep() {
return stepBuilderFactory.get("ItemWriterProcesserFileSte11")
.<Customer,Customer>chunk(2)
.reader(this.reader)
.processor(this.processer)
.writer(this.writer)
.build();
}
}
(5)提示,如果要想运行,请修改application.yml中的关闭job,并且指定启动哪个springbatch的job,指定的名字就是job的name
3.场景3:file->DB
(1)自定义stepListener
用于监听我们的step,获取我们job中传递的参数
/**
* @author gl
* @time 2020-05-01 17:57
* @function : 利用step的监听获取job中的参数
* @step :
*/
@Component(value = "stepListenerDemo1")
public class StepListenerDemo1 implements StepExecutionListener {
//获取job中的参数
//在step执行之前执行的
@Override
public void beforeStep(StepExecution stepExecution) {
Map<String, JobParameter> parameters = stepExecution.getJobParameters().getParameters();
System.out.println("step的监听器中获取的jobParameters中的参数param1:" + parameters.get("param11"));
}
//在step执行之后执行
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
return null;
}
}
(2)自定义skipListener
这是在我们的job发生异常的时候,指定跳过的数据,并且将跳过的数据写入一个error.txt文档
/**
* @author gl
* @time 2020-05-01 15:56
* @function :
* @step :
*/
@Component(value = "mySkipListener")
public class MySkipListener implements SkipListener<Customer,Customer> {
//reader中发生异常的,处理方式
//将文件中不符合我们需要的数据,也就是我们跳过的数据写入E://err.txt文件
@Override
public void onSkipInRead(Throwable throwable) {
//错误处理
System.out.println("这是错误跳过处理的reader的监听:" + throwable.getMessage());
try {
String path = System.getProperty("user.dir")+ "\\error.txt";
//第一个参数是写入的文件
//第二个参数:true代表的是追加,false代表的是重新写入,默认是false
FileOutputStream out = new FileOutputStream(path,true);
out.write(throwable.getMessage().getBytes());
String newLine = System.getProperty("line.separator");
out.write(newLine.getBytes());
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("当前文件找不到");
}
}
//writer中发生异常的记录
@Override
public void onSkipInWrite(Customer customer, Throwable throwable) {
}
//processer发生异常的记录
@Override
public void onSkipInProcess(Customer customer, Throwable throwable) {
}
}
(3)定义reader和writer对象
/**
* @author gl
* @time 2020-04-30 15:10
* @function :定义从数据库写入的writer
* @step :
*/
@Configuration
public class ItemWriterDbConfig {
@Autowired
private DataSource dataSource;
//注入mysql书写writer
@Bean(value = "ItemWriterMysql")
public JdbcBatchItemWriter<Customer> ItemWriterMysql(){
JdbcBatchItemWriter<Customer> writer = new JdbcBatchItemWriter<>();
writer.setDataSource(this.dataSource); //设置数据源
//设置sql骨架语句
writer.setSql("insert into custmer(id,first_name,last_name,birthday) " + //这里的字段是数据库中的字段名称
"values(:id,:firstName,:lastName,:birthday)"); //这里的字段是custmer对象的属性字段
//设置传入的参数
writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Customer>());
//返回writer对象
return writer;
}
//定义从文件中读取数据,直接复制从单个文件中读取的reader
@Bean(value = "fileReader")
@StepScope//这个表示step单例,意思就是在一个step中一个reader的实例.
//@Value("#{jobParameters[param2]}" 这个表示调用本方法的时候框架自动给我们从jobparameters去参数param2给本方法使用.
//但是本取值必须要在jobLauncher启动job时传递进来的参数
public FlatFileItemReader<Customer> fileReader(@Value("#{jobParameters[param2]}") String param2) {
System.out.println("reader中获取的jobparameter中的参数paranm2:" + param2); //测试输出我们取到的传入job\中的参数
FlatFileItemReader<Customer> reader = new FlatFileItemReader<>();
reader.setResource(new ClassPathResource("custmer.txt"));//设置读取文件的位置
reader.setLinesToSkip(1);//跳过第几行,因为第一行是列的名字
//解析数据
DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
tokenizer.setNames("id","firstName","lastName","birthday"); //设置列明名字,及顺序
//将解析出的字段映射为对象
DefaultLineMapper<Customer> lineMapper = new DefaultLineMapper<>();
lineMapper.setLineTokenizer(tokenizer);
lineMapper.setFieldSetMapper(new FieldSetMapper<Customer>() {
@Override
public Customer mapFieldSet(FieldSet fieldSet) throws BindException {
Customer customer = new Customer();
customer.setId(fieldSet.readLong("id")); //将以逗号分开的数据,第一个数据放入id中
customer.setFirstName(fieldSet.readString("firstName"));
customer.setLastName(fieldSet.readString("lastName"));
customer.setBirthday(fieldSet.readDate("birthday"));
return customer;
}
});
//将映射送回reader对象
reader.setLineMapper(lineMapper);
return reader;
}
}
(4)书写job的配置类
/**
* @author gl
* @time 2020-04-30 14:59
* @function : 测试写入mysql数据库中
* @step :
* 1.从文件中读取数据
* 2.读取到的数据写入数据库中
*/
@Configuration
public class SkipExceptionJobDemo {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
@Qualifier("fileReader") //注入以前写的读取文件中的fileReader对象
private FlatFileItemReader<Customer> reader;
@Autowired
@Qualifier(value = "ItemWriterMysql")//注入mysqlWriter对象
private ItemWriter<Customer> writer;
@Autowired
@Qualifier("mySkipListener")
private SkipListener<? super Customer, ? super Customer> mySkipListener;
@Autowired
@Qualifier("stepListenerDemo1")
private StepExecutionListener stepListener;
@Bean(value = "SkipExceptionJob")
public Job SkipExceptionJob(){
return jobBuilderFactory.get("SkipExceptionJob")
.start(SkipExceptionStep())
.build();
}
//测试错误跳过
//这是从文件读取写入到数据库中,我们把文件中的数据写错两行,验证跳过.
//修改custmer.txt文件中两行,故意写错
//@Value("#{jobParameters[param2]}") String param2 这种方式不能用在step对象中,
// 因为step是先于Jobparameters对象创建的
@Bean(value = "SkipExceptionStep")
public Step SkipExceptionStep() {
return stepBuilderFactory.get("SkipExceptionStep")
.listener(this.stepListener)
.<Customer,Customer>chunk(2)
.reader(this.reader)
.writer(this.writer)
.faultTolerant()//容错
.skip(Exception.class)//跳过什么异常
.noSkip(ArrayIndexOutOfBoundsException.class)//什么异常不跳过
.skipLimit(3) //跳过次数
.listener(this.mySkipListener) //这是错误跳过的listener
.build();
}
}
(5)主启动类启动,同样的需要修改application.yml文件
4.场景4:数据库读取->多个file
(1)配置reader和writer对象
/**
* @author gl
* @time 2020-04-30 15:55
* @function : 从数据读取数据,按照分类存储到多个文件
* @step :
*/
@Configuration
public class MultiFileReaderAndWriterConfig {
//读取数据库数据,从哪个数据库读取那张表的那些字段信息
@Bean(value = "itemReaderDbCustmer")
@StepScope//一个step,一个实例
public JdbcPagingItemReader<Customer> itemReaderDbCustmer() {
JdbcPagingItemReader<Customer> reader = new JdbcPagingItemReader();
reader.setDataSource(dataSource);//数据源
reader.setFetchSize(2);//分页,每页读取数据
reader.setRowMapper(new RowMapper<Customer>() {
@Override
public Customer mapRow(ResultSet rs, int i) throws SQLException {
Customer customer = new Customer();
customer.setId(rs.getLong(1));
customer.setFirstName(rs.getString(2));
customer.setLastName(rs.getString(3));
customer.setBirthday(rs.getDate(4));
return customer;
}
});
//指定sql语句
MySqlPagingQueryProvider provider = new MySqlPagingQueryProvider();
provider.setSelectClause("id,first_name,last_name,birthday");//指定读取的列名字
provider.setFromClause("from custmer"); //指定表名
//根据那个字段进行排序
Map<String,Order> sort = new HashMap<>(1);
sort.put("id",Order.DESCENDING);
provider.setSortKeys(sort);
//将provider赋值给reader
reader.setQueryProvider(provider);
return reader;
}
//按照数据类型,将数据分类存储到不同的文件
@Bean(value = "MultiFileWriter")
@StepScope
public ClassifierCompositeItemWriter<Customer> MultiFileWriter(){
ClassifierCompositeItemWriter<Customer> writer = new ClassifierCompositeItemWriter<>();
writer.setClassifier(new Classifier<Customer, ItemWriter<? super Customer>>() {
@Override//这是对custmer分类,分了类之后让不同的writer输出
public ItemWriter<? super Customer> classify(Customer customer) {
ItemWriter<Customer> writer1 = null;
try {//按照custmer对象的id的技术偶数分类.
writer1 = (customer.getId()%2 == 0)? itemWriterDbCustmerOdd(): itemWriterDbCustmerEven();
} catch (Exception e) {
e.printStackTrace();
}
return writer1;
}
});
return writer;
}
//定义输出Writer,写入文件,包括写入的格式是什么
//这是将custmer的id等于奇数的写入的文件
@Bean(value = "itemWriterDbCustmerEven")
@StepScope
public FlatFileItemWriter<Customer> itemWriterDbCustmerEven() throws Exception {
FlatFileItemWriter<Customer> writer = new FlatFileItemWriter<>();
writer.setEncoding("utf8");
//classpathResource是当前类路径
//filepathResource是磁盘路劲
writer.setResource(new FileSystemResource("E:\\custmerEven.txt"));
//把custmer转化成string
writer.setLineAggregator(new LineAggregator<Customer>() {
@Override
public String aggregate(Customer customer) {
//转化成json的字符串
ObjectMapper mapper = new ObjectMapper();
String result = "";
try {
result = mapper.writeValueAsString(customer);
} catch (JsonProcessingException e) {
throw new RuntimeException("对象转换出现异常");
}
return result;
}
});
writer.afterPropertiesSet();
return writer;
}
//定义输出Writer,写入文件,包括写入的格式是什么
//这是将custmer的id等于偶数的写入的文件
@Bean(value = "itemWriterDbCustmerOdd")
@StepScope
public FlatFileItemWriter<Customer> itemWriterDbCustmerOdd() throws Exception {
FlatFileItemWriter<Customer> writer = new FlatFileItemWriter<>();
writer.setEncoding("utf8");
//classpathResource是当前类路径
//filepathResource是磁盘路劲
writer.setResource(new FileSystemResource("E:\\custmerOdd.txt"));
//把custmer转化成string
writer.setLineAggregator(new LineAggregator<Customer>() {
@Override
public String aggregate(Customer customer) {
//转化成json的字符串
ObjectMapper mapper = new ObjectMapper();
String result = "";
try {
result = mapper.writeValueAsString(customer);
} catch (JsonProcessingException e) {
throw new RuntimeException("对象转换出现异常");
}
return result;
}
});
writer.afterPropertiesSet();
return writer;
}
}
(2)书写job的配置类
/**
* @author gl
* @time 2020-04-30 16:29
* @function :从数据库读取写入多个文件的job配置类
* @step :
*/
@Configuration
public class ItemWriterMultiFileConfiguration {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
@Qualifier("itemReaderDbCustmer")//注入读取custmer表的reader对象
private ItemReader<Customer> reader;
@Autowired
@Qualifier("MultiFileWriter") //注入写的writer对象
private ItemWriter<Customer> writer;
@Bean(value = "ItemWriterMultiFileJob")
public Job ItemWriterMultiFileJob(){
return jobBuilderFactory.get("ItemWriterMultiFileJob")
.start(ItemWriterMultiFileStep())
.build();
}
@Bean(value = "ItemWriterMultiFileStep")
public Step ItemWriterMultiFileStep() {
return stepBuilderFactory.get("ItemWriterMultiFileStep")
.<Customer,Customer>chunk(2)
.reader(this.reader)
.writer(this.writer)
.build();
}
}
以上就是关于springbatch快速入门的基本介绍,当然springbatch中还有很多需要注意的点,以后再总结