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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Spring Batch splits ETL into reader, processor, and writer beans that a step orchestrates in fixed-size chunks.
- 2Chunk processing commits per batch, so tuning chunk size trades transaction overhead against memory and rollback cost.
- 3Fault tolerance with skip rules lets a job survive a bounded number of bad rows instead of failing outright.
Related explainers
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
java
package com.acme.billing.config; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties;
Feature-flagged beans with Spring @ConditionalOnProperty
feature-flags
conditional-beans
strategy-pattern
Intermediate
5 steps
ruby
class WeeklySignupsReport DEFAULT_WEEKS = 12 def initialize(weeks: DEFAULT_WEEKS, source: User.all)
Building a weekly signups report in Rails
service object
aggregation
group by
Intermediate
7 steps
java
public static Map<String, String> parseCookieHeader(String header) { Map<String, String> cookies = new LinkedHashMap<>(); if (header == null || header.isBlank()) { return cookies;
Parsing an HTTP Cookie header in Java
string-parsing
http
url-decoding
Intermediate
6 steps
java
public class TimedSocketReader { private static final int READ_TIMEOUT_MS = 5_000; private static final int CONNECT_TIMEOUT_MS = 3_000;
Reading a socket with connect and read timeouts
sockets
timeouts
io
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/how-a-spring-batch-csv-import-job-is-wired-explained-java-4af8/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.