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
java
public interface GitHubClient { @GetExchange("/users/{username}") GitHubUser getUser(@PathVariable String username);
Declarative HTTP clients in Spring
http-client
declarative-api
proxy
Intermediate
8 steps
java
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;
Building a trie for autocomplete in Java
trie
prefix-tree
recursion
Intermediate
8 steps
java
@RestController @RequestMapping("/api/products") public class ProductSearchController {
Binding collection query params in Spring
rest-api
query-parameters
dependency-injection
Intermediate
6 steps
java
import java.util.ArrayDeque; import java.util.Deque; import java.util.Map;
Evaluating math expressions with two stacks
stacks
parsing
operator-precedence
Intermediate
9 steps
java
@Entity @Table(name = "orders") @SQLDelete(sql = "UPDATE orders SET deleted = true, deleted_at = now() WHERE id = ?") @Where(clause = "deleted = false")
Soft deletes with Hibernate in Spring
soft-delete
jpa
hibernate
Intermediate
9 steps
java
@GetMapping("/files/{id}") public ResponseEntity<StreamingResponseBody> download( @PathVariable String id, @RequestHeader(value = HttpHeaders.RANGE, required = false) String rangeHeader) throws IOException {
HTTP range requests in Spring
http-range
streaming
file-io
Advanced
10 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.