A Spring Batch chunk step reads, optionally transforms, and writes a bounded group of items within a transaction. Successful chunks commit independently, so a later failure does not roll back the entire import.
Spring Batch chunk restart: know which receipts committed
Place the restart cursor with the committed work
A receipt import with a chunk size of 100 may commit 200 rows and fail while writing the next group. A restart uses reader and JobRepository state, but the destination still needs a stable source key: the checked four-row fixture revisited committed rows, and a plain INSERT failed on their IDs. Choose a persistent repository when restart across process crashes matters. A resourceless repository cannot provide that durability.
package in.aitrove.receipts;
import org.springframework.batch.core.step.Step;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.infrastructure.item.ItemReader;
import org.springframework.batch.infrastructure.item.ItemWriter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
record ImportedReceipt(long sourceId, long amountCents) {}
@Configuration
class ReceiptImportStep {
@Bean
Step receiptChunk(JobRepository repository,
PlatformTransactionManager transactions,
ItemReader<ImportedReceipt> reader,
ItemWriter<ImportedReceipt> writer) {
return new StepBuilder("receiptChunk", repository)
.<ImportedReceipt, ImportedReceipt>chunk(100)
.transactionManager(transactions)
.reader(reader)
.writer(writer)
.build();
}
}The example assumes reader and writer beans are configured separately and the Spring Batch dependency is installed. A chunk size is not a universal throughput knob. Larger chunks retain more items and hold transactions longer; smaller chunks increase commit and metadata work. Measure both against the database and input source.
Make duplicate writes rejectable
A file reader can replay a line after a crash if progress and destination writes are not coordinated as expected. Put a unique source identifier on each receipt and define whether a duplicate is skipped, reported or treated as a changed record. An external side effect inside a chunk is not rolled back by a database transaction.
Common Mistakes
- Calling a job restartable without testing a process crash after a committed chunk.
- Keeping an entire import in one transaction until the last line.
- Using a non-durable repository for a job that must survive restarts.
- Assuming a database rollback also retracts an already-sent message.
Read next
Receipt upload validation, atomic event records, and bounded backfills.
Checked local restart
The downloadable chunk test leaves two rows committed after a later failure. Its saved reader count is two, while an H2 key-based writer lets the same instance finish on restart. That assertion is in-process; the separate JDBC fixture checks a hard kill between chunks and explicit recovery.
Related Batch contract
Spring Batch FlatFileItemReader: save state against an immutable source.
Related Batch contract
Spring Batch ExecutionContext keys: give each stream its own checkpoint.
