乐于分享
好东西不私藏

别再手写 Excel 导入了!Spring Batch 一套流程搞定

别再手写 Excel 导入了!Spring Batch 一套流程搞定
Spring Boot 3实战案例锦集PDF电子书已更新至130篇!
🎉🎉《Spring Boot实战案例合集》目前已更新235个案例,我们将持续不断的更新。文末有电子书目录。→ 现在就订阅合集

环境:Spring Boot 3.5.0


1. 简介

在实际项目中,Excel 导入一直是一个绕不开的需求。用户上传文件后,开发者往往需要处理文件解析、数据校验、异常记录、批量写入、失败重试等一系列问题。数据量一大,简单的循环读取不仅效率低,还容易导致内存占用过高。Spring Batch 提供了一套完整的批处理机制,将读取、处理、写入这些步骤进行标准化封装,让 Excel 导入不再依赖大量重复代码。通过合理设计 Job 和 Step,就能轻松应对大批量数据处理场景。

2.实战案例
2.1 准备环境
<dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-batch</artifactId></dependency><dependency>  <groupId>org.springframework.batch.extensions</groupId>  <artifactId>spring-batch-excel</artifactId>  <version>0.2.0</version></dependency><dependency>  <groupId>org.apache.poi</groupId>  <artifactId>poi-ooxml</artifactId>  <version>5.5.1</version></dependency><dependency>  <groupId>org.apache.poi</groupId>  <artifactId>poi</artifactId>  <version>5.5.1</version></dependency>
数据模型
@Entity@Table(name = "t_person")public class Person {  @Id  private Long id ;  private String name;  private Integer age ;  private String email ;  private String address ;  private String phone ;}
Repository接口
public interface PersonRepository extends JpaRepository<PersonLong> {}
2.2 定义Reader从Excel读数据

Spring Batch 扩展模块通过提供 PoiItemReader 类来简化了Excel的读取,该类实现了 ItemReader 接口,并利用 Apache POI 读取 Excel 文件。它将按工作表逐个、按行逐行地读取文件。需要注意的是,该类并非线程安全的。

@BeanItemReader<PersonpersonReader(ApplicationContext context) {  PoiItemReader<Person> reader = new PoiItemReader<>();  reader.setLinesToSkip(1);  reader.setResource(context.getResource(context.getEnvironment().getProperty("pack.resource.file")));  reader.setRowMapper(new PersonRowMapper()) ;  return reader;}
定义Excel数据行到Person的映射
public class PersonRowMapper implements RowMapper<Person> {  @Override  public Person mapRow(RowSet rs) throws Exception {    Person person = new Person();    person.setId(Long.valueOf(rs.getProperties().getProperty("编号"))) ;    person.setName(rs.getProperties().getProperty("姓名")) ;    person.setAge(Integer.valueOf(rs.getProperties().getProperty("年龄"))) ;    person.setEmail(rs.getProperties().getProperty("邮箱")) ;    person.setAddress(rs.getProperties().getProperty("住址")) ;    person.setPhone(rs.getProperties().getProperty("电话")) ;    return person;  }}
2.3 定义Writer将数据写入DB

创建 ItemWriter,它将 Person 实体写入数据库。在写入实现中,我们可以使用事务控制提交和回滚过程,或者直接使用 JpaRepository,它会在处理每条记录时自动保存并提交该记录。

public class DatabasePersonWriter implements ItemWriter<Person> {  private static final Logger logger = LoggerFactory.getLogger(DatabasePersonWriter.class);  private final PersonRepository personRepository;  public DatabasePersonWriter(PersonRepository personRepository) {    this.personRepository = personRepository;  }  @Override  public void write(Chunk<? extends Person> personChunk) throws Exception {    logger.info("写 {} 条数据", personChunk.size());    this.personRepository.saveAllAndFlush(personChunk.getItems()) ;  }}
这里的Chunk代表了每批次将会写入多少的数据。
2.4 定义ItemProcessor处理

创建 ItemProcessor,并在其中定义数据验证和转换逻辑。该逻辑必须针对具体项目进行编写。我们仅对值进行日志记录,以验证该方法是否已被调用。这个步骤是可选的。

public class LoggingPersonProcessor implements ItemProcessor<Person, Person> {  private static final Logger logger = LoggerFactory.getLogger(LoggingPersonProcessor.class);  @Override  public Person process(Person item) throws Exception {    logger.info("Processing person: {}", item);    return item;  }}
2.5 定义Step

"Step" 用于明确表示要执行的步骤配置,这些步骤主要包括读取、写入和/或处理记录。

@BeanStep excel2dbStep(ItemReader<Person> personReader, PlatformTransactionManager transactionManager,     DatabasePersonWriter databasePersonWriter, JobRepository jobRepository) {  return new StepBuilder("excel2db-step", jobRepository)      .<PersonPerson>chunk(10, transactionManager)      .reader(personReader)      .processor(loggingPersonProcessor())      .writer(databasePersonWriter)      .build();}
以上我们就完成了所有的步骤。接下来,可以创建Job进行执行了。
2.6 运行Job

一个Job可能包含多个步骤。每个Step可能涉及不同的读写操作。通过创建一个 Job我们可以定义哪些步骤属于该作业。

@RestController@RequestMapping("/excel2db")public class Excel2DbController {  private final JobLauncher jobLauncher;  private final Step excel2dbStep ;   private final JobRepository jobRepository ;  public Excel2DbController(JobLauncher jobLauncher,       Step excel2dbStep, JobRepository jobRepository) {    this.jobLauncher = jobLauncher;    this.excel2dbStep = excel2dbStep ;    this.jobRepository = jobRepository ;  }  @GetMapping  public ResponseEntity<?> execl2Db() throws Exception {    var builder = new JobBuilder("excelToDatabase", jobRepository);    Job job = builder        .start(excel2dbStep)        .build() ;    Map<String, JobParameter<?>> parameters = Map.of("timestamp", new JobParameter<>(System.currentTimeMillis(), Long.class)) ;    JobParameters jobParameters = new JobParameters(parameters ) ;    this.jobLauncher.run(job, jobParameters) ;    return ResponseEntity.ok("success") ;  }}

准备如下excel

调用上面的接口后,查看控制台及数据库:
以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏
别再写 try-catch 了!Spring Boot 优雅实现接口降级

自己动手实现Agent统计API接口调用耗时

项目亮点!Spring Boot 多线程事务一致性方案,支持JDBC,MyBatis,JPA

技巧,非常实用!Spring Boot接口参数这样获取真优雅

优雅!自定义注解轻松管理多版本Controller接口

基于Spring Boot六种策略识别上传文件类型

Map你只会用put,get?试试这些高级方法

Spring Boot 接口多实现注入的5种方法,最后一这种非常巧妙

Jackson!JSON处理的瑞士军刀ObjectMapper

Java Agent + ByteBuddy 实现 Spring Boot 运行时方法级监控