java 67 lines · 10 steps

How a Spring Batch CSV import job is wired

A chunk-oriented Spring Batch job reads a CSV, transforms each row into a User, and bulk-inserts them with fault tolerance.

Explained by highlit
1@Configuration
2public class UserImportJobConfig {
3 
4 @Bean
5 public Job userImportJob(JobRepository jobRepository, Step importUsersStep) {
6 return new JobBuilder("userImportJob", jobRepository)
7 .incrementer(new RunIdIncrementer())
8 .start(importUsersStep)
9 .build();
10 }
11 
12 @Bean
13 public Step importUsersStep(JobRepository jobRepository,
14 PlatformTransactionManager transactionManager,
15 ItemReader<UserCsvRecord> reader,
16 ItemProcessor<UserCsvRecord, User> processor,
17 ItemWriter<User> writer) {
18 return new StepBuilder("importUsersStep", jobRepository)
19 .<UserCsvRecord, User>chunk(100, transactionManager)
20 .reader(reader)
21 .processor(processor)
22 .writer(writer)
23 .faultTolerant()
24 .skip(FlatFileParseException.class)
25 .skipLimit(25)
26 .build();
27 }
28 
29 @Bean
30 @StepScope
31 public FlatFileItemReader<UserCsvRecord> reader(@Value("#{jobParameters['inputFile']}") Resource inputFile) {
32 return new FlatFileItemReaderBuilder<UserCsvRecord>()
33 .name("userCsvReader")
34 .resource(inputFile)
35 .linesToSkip(1)
36 .delimited()
37 .names("email", "firstName", "lastName", "country")
38 .targetType(UserCsvRecord.class)
39 .build();
40 }
41 
42 @Bean
43 public ItemProcessor<UserCsvRecord, User> processor() {
44 return record -> {
45 String email = record.email().trim().toLowerCase();
46 if (!email.contains("@")) {
47 return null;
48 }
49 User user = new User();
50 user.setEmail(email);
51 user.setFullName("%s %s".formatted(record.firstName(), record.lastName()).trim());
52 user.setCountry(record.country().toUpperCase());
53 user.setStatus(UserStatus.PENDING);
54 return user;
55 };
56 }
57 
58 @Bean
59 public JdbcBatchItemWriter<User> writer(DataSource dataSource) {
60 return new JdbcBatchItemWriterBuilder<User>()
61 .dataSource(dataSource)
62 .sql("INSERT INTO users (email, full_name, country, status) "
63 + "VALUES (:email, :fullName, :country, :status)")
64 .beanMapped()
65 .build();
66 }
67}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Spring Batch splits ETL into reader, processor, and writer beans that a step orchestrates in fixed-size chunks.
  2. 2Chunk processing commits per batch, so tuning chunk size trades transaction overhead against memory and rollback cost.
  3. 3Fault tolerance with skip rules lets a job survive a bounded number of bad rows instead of failing outright.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How a Spring Batch CSV import job is wired — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code