Spring batch是用来处理大量数据操作的一个框架,主要用来读取大量数据,然后进行一定处理后输出成指定的形式。
Spring batch主要有以下部分组成:
以上Spring Batch的主要组成部分只需要注册成Spring的Bean即可。若想开启批处理的支持还需在配置类上使用@EnableBatchProcessing,在Spring Batch中提供了大量的ItemReader和ItemWriter的实现,用来读取不同的数据来源,数据的处理和校验都要通过ItemProcessor接口实现来完成。
Spring Boot的支持
Spring Boot对Spring Batch支持的源码位于org.springframework.boot.autoconfigure.batch下。
Spring Boot为我们自动初始化了Spring Batch存储批处理记录的数据库。
spring batch会自动加载hsqldb驱动,根据需求选择去留。
下面是一个spring boot支持spring batch 的例子:
1. 实体类
1 public class Person { 2 3 @Size(max=4,min=2) //使用JSR-303注解来校验注解 4 private String name; 5 6 private int age; 7 8 private String nation; 9 10 private String address; 11 12 public String getName() { 13 return name; 14 } 15 16 public void setName(String name) { 17 this.name = name; 18 } 19 20 public int getAge() { 21 return age; 22 } 23 24 public void setAge(int age) { 25 this.age = age; 26 } 27 28 public String getNation() { 29 return nation; 30 } 31 32 public void setNation(String nation) { 33 this.nation = nation; 34 } 35 36 public String getAddress() { 37 return address; 38 } 39 40 public void setAddress(String address) { 41 this.address = address; 42 } 43 }
2. 校验器
1 public class CsvBeanValidatorimplements Validator ,InitializingBean { 2 private javax.validation.Validator validator; 3 @Override 4 public void afterPropertiesSet() throws Exception { //使用JSR-303的Validator来校验我们的数据,在此处进行JSR-303的Validator的初始化 5 ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); 6 validator = validatorFactory.usingContext().getValidator(); 7 } 8 9 @Override 10 public void validate(T value) throws ValidationException { 11 Set > constraintViolations = validator.validate(value); //使用Validator的validate方法校验数据 12 if(constraintViolations.size()>0){ 13 14 StringBuilder message = new StringBuilder(); 15 for (ConstraintViolation constraintViolation : constraintViolations) { 16 message.append(constraintViolation.getMessage() + "\n"); 17 } 18 throw new ValidationException(message.toString()); 19 20 } 21 22 } 23 24 }
3. ItemProcessor
1 public class CsvItemProcessor extends ValidatingItemProcessor{ 2 3 @Override 4 public Person process(Person item) throws ValidationException { 5 super.process(item); //需要执行super.process(item)才会调用自定义校验器 6 7 if(item.getNation().equals("汉族")){ //对数据做简单的处理,若民族为汉族,则数据转换成01,其余转换成02 8 item.setNation("01"); 9 }else{ 10 item.setNation("02"); 11 } 12 return item; 13 } 14 15 16 }
4. Job监听(监听器要实现JobExecutionListener接口,并重写其beforeJob、afterJob方法即可)
1 public class CsvJobListener implements JobExecutionListener{ 2 3 long startTime; 4 long endTime; 5 @Override 6 public void beforeJob(JobExecution jobExecution) { 7 startTime = System.currentTimeMillis(); 8 System.out.println("任务处理开始"); 9 } 10 11 @Override 12 public void afterJob(JobExecution jobExecution) { 13 endTime = System.currentTimeMillis(); 14 System.out.println("任务处理结束"); 15 System.out.println("耗时:" + (endTime - startTime) + "ms"); 16 } 17 18 }
5. 配置
1 @Configuration 2 @EnableBatchProcessing 3 public class CsvBatchConfig { 4 5 @Bean 6 public ItemReaderreader() throws Exception { 7 FlatFileItemReader reader = new FlatFileItemReader (); //使用FlatFileItemReader读取文件 8 reader.setResource(new ClassPathResource("people.csv")); //使用FlatFileItemReader的setResource方法设置CSV文件的路径 9 reader.setLineMapper(new DefaultLineMapper () {{ //在此处对CVS文件的数据和领域模型类做对应映射 10 setLineTokenizer(new DelimitedLineTokenizer() {{ 11 setNames(new String[] { "name","age", "nation" ,"address"}); 12 }}); 13 setFieldSetMapper(new BeanWrapperFieldSetMapper () {{ 14 setTargetType(Person.class); 15 }}); 16 }}); 17 return reader; 18 } 19 20 @Bean 21 public ItemProcessor processor() { 22 CsvItemProcessor processor = new CsvItemProcessor(); //使用自定义的ItemProcessor的实现 23 processor.setValidator(csvBeanValidator()); //为Processor指定校验器 24 return processor; 25 } 26 27 28 29 @Bean 30 public ItemWriter writer(DataSource dataSource) {//Spring能让容器中已有的Bean以参数的形式注入,Spring boot已经定义了DataSource 31 JdbcBatchItemWriter writer = new JdbcBatchItemWriter (); //使用JDBC批处理的JdbcBatchItemWriter来写数据到数据库 32 writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider ()); 33 String sql = "insert into person " + "(id,name,age,nation,address) " 34 + "values(hibernate_sequence.nextval, :name, :age, :nation,:address)"; 35 writer.setSql(sql); //在此设置要执行批处理的sql语句 36 writer.setDataSource(dataSource); 37 return writer; 38 } 39 40 @Bean 41 public JobRepository jobRepository(DataSource dataSource, PlatformTransactionManager transactionManager) 42 throws Exception { 43 JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean(); 44 jobRepositoryFactoryBean.setDataSource(dataSource); 45 jobRepositoryFactoryBean.setTransactionManager(transactionManager); 46 jobRepositoryFactoryBean.setDatabaseType("oracle"); 47 return jobRepositoryFactoryBean.getObject(); 48 } 49 50 @Bean 51 public SimpleJobLauncher jobLauncher(DataSource dataSource, PlatformTransactionManager transactionManager) 52 throws Exception { 53 SimpleJobLauncher jobLauncher = new SimpleJobLauncher(); 54 jobLauncher.setJobRepository(jobRepository(dataSource, transactionManager)); 55 return jobLauncher; 56 } 57 58 @Bean 59 public Job importJob(JobBuilderFactory jobs, Step s1) { 60 return jobs.get("importJob") 61 .incrementer(new RunIdIncrementer()) 62 .flow(s1) //指定step 63 .end() 64 .listener(csvJobListener()) //绑定监听器 65 .build(); 66 } 67 68 @Bean 69 public Step step1(StepBuilderFactory stepBuilderFactory, ItemReader reader, ItemWriter writer, 70 ItemProcessor processor) { 71 return stepBuilderFactory 72 .get("step1") 73 . chunk(65000) //批处理每次提交65000条数据 74 .reader(reader) //给step绑定reader 75 .processor(processor) //给step绑定Processor 76 .writer(writer) //给step绑定writer 77 .build(); 78 } 79 80 81 82 @Bean 83 public CsvJobListener csvJobListener() { 84 return new CsvJobListener(); 85 } 86 87 @Bean 88 public Validator csvBeanValidator() { 89 return new CsvBeanValidator (); 90 } 91 92 93 }
6.application.xml
1 spring.datasource.driverClassName=oracle.jdbc.OracleDriver 2 spring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521\:xe 3 spring.datasource.username=boot 4 spring.datasource.password=boot 5 6 spring.batch.job.enabled=true 7 8 logging.level.org.springframework.web = DEBUG
上面的例子是自动触发批处理的,当我们需要手动触发批处理时,需要将CsvBatchConfig类的@Configuration注解注释掉,让此配置类不再起效,新建TriggerBatchConfig配置类,内容与CsvBatchConfig完全一致,除了修改定义ItemReader这个Bean;另外,还需要修改application.xml配置文件spring.batch.job.enable=false
1 @Configuration 2 @EnableBatchProcessing 3 public class TriggerBatchConfig { 4 5 @Bean 6 @StepScope 7 public FlatFileItemReaderreader(@Value("#{jobParameters['input.file.name']}") String pathToFile) throws Exception { 8 FlatFileItemReader reader = new FlatFileItemReader (); // 9 reader.setResource(new ClassPathResource(pathToFile)); // 10 reader.setLineMapper(new DefaultLineMapper () {{ // 11 setLineTokenizer(new DelimitedLineTokenizer() {{ 12 setNames(new String[] { "name","age", "nation" ,"address"}); 13 }}); 14 setFieldSetMapper(new BeanWrapperFieldSetMapper () {{ 15 setTargetType(Person.class); 16 }}); 17 }}); 18 19 return reader; 20 } 21 22 @Bean 23 public ItemProcessor processor() { 24 CsvItemProcessor processor = new CsvItemProcessor(); 25 processor.setValidator(csvBeanValidator()); 26 return processor; 27 } 28 29 30 31 @Bean 32 public ItemWriter writer(DataSource dataSource) { 33 JdbcBatchItemWriter writer = new JdbcBatchItemWriter (); 34 writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider ()); 35 String sql = "insert into person " + "(id,name,age,nation,address) " 36 + "values(hibernate_sequence.nextval, :name, :age, :nation,:address)"; 37 writer.setSql(sql); //3 38 writer.setDataSource(dataSource); 39 return writer; 40 } 41 42 @Bean 43 public JobRepository jobRepository(DataSource dataSource, PlatformTransactionManager transactionManager) 44 throws Exception { 45 JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean(); 46 jobRepositoryFactoryBean.setDataSource(dataSource); 47 jobRepositoryFactoryBean.setTransactionManager(transactionManager); 48 jobRepositoryFactoryBean.setDatabaseType("oracle"); 49 return jobRepositoryFactoryBean.getObject(); 50 } 51 52 @Bean 53 public SimpleJobLauncher jobLauncher(DataSource dataSource, PlatformTransactionManager transactionManager) 54 throws Exception { 55 SimpleJobLauncher jobLauncher = new SimpleJobLauncher(); 56 jobLauncher.setJobRepository(jobRepository(dataSource, transactionManager)); 57 return jobLauncher; 58 } 59 60 @Bean 61 public Job importJob(JobBuilderFactory jobs, Step s1) { 62 return jobs.get("importJob") 63 .incrementer(new RunIdIncrementer()) 64 .flow(s1) 65 .end() 66 .listener(csvJobListener()) 67 .build(); 68 } 69 70 @Bean 71 public Step step1(StepBuilderFactory stepBuilderFactory, ItemReader reader, ItemWriter writer, 72 ItemProcessor processor) { 73 return stepBuilderFactory 74 .get("step1") 75 . chunk(65000) 76 .reader(reader) 77 .processor(processor) 78 .writer(writer) 79 .build(); 80 } 81 82 83 84 @Bean 85 public CsvJobListener csvJobListener() { 86 return new CsvJobListener(); 87 } 88 89 @Bean 90 public Validator csvBeanValidator() { 91 return new CsvBeanValidator (); 92 } 93 94 95 }
控制层代码
1 @RestController 2 public class DemoController { 3 4 @Autowired 5 JobLauncher jobLauncher; 6 7 @Autowired 8 Job importJob; 9 public JobParameters jobParameters; 10 11 @RequestMapping("/read") 12 public String imp(String fileName) throws Exception{ 13 14 String path = fileName+".csv"; 15 jobParameters = new JobParametersBuilder() 16 .addLong("time", System.currentTimeMillis()) 17 .addString("input.file.name", path) 18 .toJobParameters(); 19 jobLauncher.run(importJob,jobParameters); 20 return "ok"; 21 } 22 23 }
大家应该想到些什么了吧。SpringBatch像是一个天然的Job,Quartz是完全可以做为它运作的调度器。两者结合,效果很不错。
本文将从0到1讲解一个Spring Batch是如何搭建并运行起来的。
本教程将讲解从一个文本文件读取数据,然后写入MySQL。
Spring Batch 作为 Spring 的子项目,是一款基于 Spring 的企业批处理框架。通过它可以构建出健壮的企业批处理应用。Spring Batch 不仅提供了统一的读写接口、丰富的任务处理方式、灵活的事务管理及并发处理,同时还支持日志、监控、任务重启与跳过等特性,大大简化了批处理应用开发,将开发人员从复杂的任务配置管理过程中解放出来,使他们可以更多地去关注核心的业务处理过程。
更多的介绍可以参考官网:https://spring.io/projects/sp...
我是用的Intellij Idea,用gradle构建。
可以使用Spring Initializr 来创建Spring boot应用。地址:https://start.spring.io/
首先选择Gradle Project,然后选择Java。填上你的Group和Artifact名字。
最后再搜索你需要用的包,比如Batch是一定要的。另外,由于我写的Batch项目是使用JPA向MySQL插入数据,所以也添加了JPA和MySQL。其他可以根据自己需要添加。
点击Generate Project,一个项目就创建好了。
Build.gralde文件大概就长这个样子:
buildscript {
ext {
springBootVersion = '2.0.4.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.demo'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-batch')
compile('org.springframework.boot:spring-boot-starter-jdbc')
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-joda', version: '2.9.4'
compile group: 'org.jadira.usertype', name: 'usertype.core', version: '6.0.1.GA'
compile group: 'mysql', name: 'mysql-connector-java', version: '6.0.6',
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.batch:spring-batch-test')
}
网上有很多Spring Batch结构和原理的讲解,我就不详细阐述了,我这里只讲一下Spring Batch的一个基本层级结构。
首先,Spring Batch运行的基本单位是一个Job,一个Job就做一件批处理的事情。
一个Job包含很多Step,step就是每个job要执行的单个步骤。
如下图所示,Step里面,会有Tasklet,Tasklet是一个任务单元,它是属于可以重复利用的东西。
然后是Chunk,chunk就是数据块,你需要定义多大的数据量是一个chunk。
Chunk里面就是不断循环的一个流程,读数据,处理数据,然后写数据。Spring Batch会不断的循环这个流程,直到批处理数据完成。
首先,我们需要一个全局的Configuration来配置所有的Job和一些全局配置。
代码如下:
@Configuration
@EnableAutoConfiguration
@EnableBatchProcessing(modular = true)
public class SpringBatchConfiguration {
@Bean
public ApplicationContextFactory firstJobContext() {
return new GenericApplicationContextFactory(FirstJobConfiguration.class);
}
@Bean
public ApplicationContextFactory secondJobContext() {
return new GenericApplicationContextFactory(SecondJobConfiguration.class);
}
}
@EnableBatchProcessing是打开Batch。如果要实现多Job的情况,需要把EnableBatchProcessing注解的modular设置为true,让每个Job使用自己的ApplicationConext。
比如上面代码的就创建了两个Job。
本博客的例子是迁移数据,数据源是一个文本文件,数据量是上百万条,一行就是一条数据。然后我们通过Spring Batch帮我们把文本文件的数据全部迁移到MySQL数据库对应的表里面。
假设我们迁移的数据是Message,那么我们就需要提前创建一个叫Message的和数据库映射的数据类。
@Entity
@Table(name = "message")
public class Message {
@Id
@Column(name = "object_id", nullable = false)
private String objectId;
@Column(name = "content")
private String content;
@Column(name = "last_modified_time")
private LocalDateTime lastModifiedTime;
@Column(name = "created_time")
private LocalDateTime createdTime;
}
首先我们需要一个关于这个Job的Configuration,它将在SpringBatchConfigration里面被加载。
@Configuration
@EnableAutoConfiguration
@EnableBatchProcessing(modular = true)
public class SpringBatchConfiguration {
@Bean
public ApplicationContextFactory messageMigrationJobContext() {
return new GenericApplicationContextFactory(MessageMigrationJobConfiguration.class);
}
}
下面的关于构建Job的代码都将写在这个MessageMigrationJobConfiguration里面。
public class MessageMigrationJobConfiguration {
}
我们先定义一个Job的Bean。
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Bean
public Job messageMigrationJob(@Qualifier("messageMigrationStep") Step messageMigrationStep) {
return jobBuilderFactory.get("messageMigrationJob")
.start(messageMigrationStep)
.build();
}
jobBuilderFactory是注入进来的,get里面的就是job的名字。
这个job只有一个step。
接下来就是创建Step。
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Step messageMigrationStep(@Qualifier("jsonMessageReader") FlatFileItemReader jsonMessageReader,
@Qualifier("messageItemWriter") JpaItemWriter messageItemWriter,
@Qualifier("errorWriter") Writer errorWriter) {
return stepBuilderFactory.get("messageMigrationStep")
.chunk(CHUNK_SIZE)
.reader(jsonMessageReader).faultTolerant().skip(JsonParseException.class).skipLimit(SKIP_LIMIT)
.listener(new MessageItemReadListener(errorWriter))
.writer(messageItemWriter).faultTolerant().skip(Exception.class).skipLimit(SKIP_LIMIT)
.listener(new MessageWriteListener())
.build();
}
stepBuilderFactory是注入进来的,然后get里面是Step的名字。
我们的Step中可以构建很多东西,比如reader,processer,writer,listener等等。
下面我们就逐个来看看step里面的这些东西是如何使用的。
Spring batch在配置Step时采用的是基于Chunk的机制,即每次读取一条数据,再处理一条数据,累积到一定数量后再一次性交给writer进行写入操作。这样可以最大化的优化写入效率,整个事务也是基于Chunk来进行。
比如我们定义chunk size是50,那就意味着,spring batch处理了50条数据后,再统一向数据库写入。
这里有个很重要的点,chunk前面需要定义数据输入类型和输出类型,由于我们输入是Message,输出也是Message,所以两个都直接写Message了。
如果不定义这个类型,会报错。
.chunk(CHUNK_SIZE)
Reader顾名思义就是从数据源读取数据。
Spring Batch给我们提供了很多好用实用的reader,基本能满足我们所有需求。比如FlatFileItemReader,JdbcCursorItemReader,JpaPagingItemReader等。也可以自己实现Reader。
本例子里面,数据源是文本文件,所以我们就使用FlatFileItemReader。FlatFileItemReader是从文件里面一行一行的读取数据。
首先需要设置文件路径,也就是设置resource。
因为我们需要把一行文本映射为Message类,所以我们需要自己设置并实现LineMapper。
@Bean
public FlatFileItemReader jsonMessageReader() {
FlatFileItemReader reader = new FlatFileItemReader<>();
reader.setResource(new FileSystemResource(new File(MESSAGE_FILE)));
reader.setLineMapper(new MessageLineMapper());
return reader;
}
LineMapper的输入就是获取一行文本,和行号,然后转换成Message。
在本例子里面,一行文本就是一个json对象,所以我们使用JsonParser来转换成Message。
public class MessageLineMapper implements LineMapper {
private MappingJsonFactory factory = new MappingJsonFactory();
@Override
public Message mapLine(String line, int lineNumber) throws Exception {
JsonParser parser = factory.createParser(line);
Map map = (Map) parser.readValueAs(Map.class);
Message message = new Message();
... // 转换逻辑
return message;
}
}
由于本例子里面,数据是一行文本,通过reader变成Message的类,然后writer直接把Message写入MySQL。所以我们的例子里面就不需要Processor,关于如何写Processor其实和reader/writer是一样的道理。
从它的接口可以看出,需要定义输入和输出的类型,把输入I通过某些逻辑处理之后,返回输出O。
public interface ItemProcessor {
O process(I item) throws Exception;
}
Writer顾名思义就是把数据写入到目标数据源里面。
Spring Batch同样给我们提供很多好用实用的writer。比如JpaItemWriter,FlatFileItemWriter,HibernateItemWriter,JdbcBatchItemWriter等。同样也可以自定义。
本例子里面,使用的是JpaItemWriter,可以直接把Message对象写到数据库里面。但是需要设置一个EntityManagerFactory,可以注入进来。
@Autowired
private EntityManagerFactory entityManager;
@Bean
public JpaItemWriter messageItemWriter() {
JpaItemWriter writer = new JpaItemWriter<>();
writer.setEntityManagerFactory(entityManager);
return writer;
}
另外,你需要配置数据库的连接等东西。由于我使用的spring,所以直接在Application.properties里面配置如下:
spring.datasource.url=jdbc:mysql://database
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.show-sql=true
spring.jpa.properties.jadira.usertype.autoRegisterUserTypes=true
spring.jackson.serialization.write-dates-as-timestamps=false
spring.batch.initialize-schema=ALWAYS
spring.jpa.hibernate.ddl-auto=update
spring.datasource相关的设置都是在配置数据库的连接。
spring.batch.initialize-schema=always表示让spring batch在数据库里面创建默认的数据表。
spring.jpa.show-sql=true表示在控制台输出hibernate读写数据库时候的SQL。
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect是在指定MySQL的方言。
Spring Batch同样实现了非常完善全面的listener,listener很好理解,就是用来监听每个步骤的结果。比如可以有监听step的,有监听job的,有监听reader的,有监听writer的。没有你找不到的listener,只有你想不到的listener。
在本例子里面,我只关心,read的时候有没有出错,和write的时候有没有出错,所以,我只实现了ReadListener和WriteListener。
在read出错的时候,把错误结果写入一个单独的error列表文件中。
public class MessageItemReadListener implements ItemReadListener {
private Writer errorWriter;
public MessageItemReadListener(Writer errorWriter) {
this.errorWriter = errorWriter;
}
@Override
public void beforeRead() {
}
@Override
public void afterRead(Message item) {
}
@Override
public void onReadError(Exception ex) {
errorWriter.write(format("%s%n", ex.getMessage()));
}
}
在write出错的时候,也做同样的事情,把出错的原因写入单独的日志中。
public class MessageWriteListener implements ItemWriteListener {
@Autowired
private Writer errorWriter;
@Override
public void beforeWrite(List extends Message> items) {
}
@Override
public void afterWrite(List extends Message> items) {
}
@Override
public void onWriteError(Exception exception, List extends Message> items) {
errorWriter.write(format("%s%n", exception.getMessage()));
for (Message message : items) {
errorWriter.write(format("Failed writing message id: %s", message.getObjectId()));
}
}
}
前面有说chuck机制,所以write的listener传入参数是一个List,因为它是累积到一定的数量才一起写入。
Spring Batch提供了skip的机制,也就是说,如果出错了,可以跳过。如果你不设置skip,那么一条数据出错了,整个job都会挂掉。
设置skip的时候一定要设置什么Exception才需要跳过,并且跳过多少条数据。如果失败的数据超过你设置的skip limit,那么job就会失败。
你可以分别给reader和writer等设置skip机制。
writer(messageItemWriter).faultTolerant().skip(Exception.class).skipLimit(SKIP_LIMIT)
这个和Skip是一样的原理,就是失败之后可以重试,你同样需要设置重试的次数。
同样可以分别给reader,writer等设置retry机制。
如果同时设置了retry和skip,会先重试所有次数,然后再开始skip。比如retry是10次,skip是20,会先重试10次之后,再开始算第一次skip。
所有东西都准备好以后,就是如何运行了。
运行就是在main方法里面用JobLauncher去运行你制定的job。
下面是我写的main方法,main方法的第一个参数是job的名字,这样我们就可以通过不同的job名字跑不同的job了。
首先我们通过运行起来的Spring application得到jobRegistry,然后通过job的名字找到对应的job。
接着,我们就可以用jobLauncher去运行这个job了,运行的时候会传一些参数,比如你job里面需要的文件路径或者文件日期等,就可以通过这个jobParameters传进去。如果没有参数,可以默认传当前时间进去。
public static void main(String[] args) {
String jobName = args[0];
try {
ConfigurableApplicationContext context = SpringApplication.run(ZuociBatchApplication.class, args);
JobRegistry jobRegistry = context.getBean(JobRegistry.class);
Job job = jobRegistry.getJob(jobName);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
JobExecution jobExecution = jobLauncher.run(job, createJobParams());
if (!jobExecution.getExitStatus().equals(ExitStatus.COMPLETED)) {
throw new RuntimeException(format("%s Job execution failed.", jobName));
}
} catch (Exception e) {
throw new RuntimeException(format("%s Job execution failed.", jobName));
}
}
private static JobParameters createJobParams() {
return new JobParametersBuilder().addDate("date", new Date()).toJobParameters();
}
最后,把jar包编译出来,在命令行执行下面的命令,就可以运行你的Spring Batch了。
java -jar YOUR_BATCH_NAME.jar YOUR_JOB_NAME
调试主要依靠控制台输出的log,可以在application.properties里面设置log输出的级别,比如你希望输出INFO信息还是DEBUG信息。
基本上,通过查看log都能定位到问题。
logging.path=build/logs
logging.file=${logging.path}/batch.log
logging.level.com.easystudio=INFO
logging.level.root=INFO
log4j.logger.org.springframework.jdbc=INFO
log4j.logger.org.springframework.batch=INFO
logging.level.org.hibernate.SQL=INFO
如果你的batch最终会写入数据库,那么Spring Batch会默认在你的数据库里面创建一些batch相关的表,来记录所有job/step运行的状态和结果。
大部分表你都不需要关心,你只需要关心几张表。
batch_job_instance:这张表能看到每次运行的job名字。
batch_job_execution:这张表能看到每次运行job的开始时间,结束时间,状态,以及失败后的错误消息是什么。
batch_step_execution:这张表你能看到更多关于step的详细信息。比如step的开始时间,结束时间,提交次数,读写次数,状态,以及失败后的错误信息等。
Spring Batch为我们提供了非常实用的功能,对批处理场景进行了完善的抽象,它不仅能实现小数据的迁移,也能应对大企业的大数据实践应用。它让我们开发批处理应用可以事半功倍。
在配置文件中 启动自动执行批处理
spring.batch.job.names = job1,job2 #启动时要执行的Job,默认执行全部Job
spring.batch.job.enabled=true #是否自动执行定义的Job,默认是
spring.batch.initializer.enabled=true #是否初始化Spring Batch的数据库,默认为是
spring.batch.schema=
spring.batch.table-prefix= #设置SpringBatch的数据库表的前缀
项目汇总
从 项目中我们可以看到 总的步骤就是 首先读取我们需要实现的文件进行解析,然后转换成需要的实体类并且绑定到reader中,二 实现我们需要的writer 并且帮到到数据库上,三实现job监听器将其绑定到步骤中 。最后开启批处理 自动执行入库即可 。这个简单步骤主要是配置中用到的 理解流程 自己也可以方便实现 批处理的流程。