Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Spring Batch chunk restart: know which receipts committed

Last updated: 5 Oct 20265 min read
tutorial
IntermediateBy AITrove Editorial

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.

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.

Java
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.

spring
spring-boot
batch-chunk-restart
Storage details