skip to content

ItemReader Implementations

Readers pull one item at a time from files, cursors, paged queries or XML, and implement ItemStream so their position survives a restart. Interviewers ask about cursor versus paging readers, because one holds a connection open for hours and the other does not.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

5

What is the ItemReader interface in Spring Batch and what is the contract of its read() method?

level: juniorimportance: must knowfreq 75%

answer

  1. read() returns one item, null = end
  2. null is sentinel not data
  3. exception ≠ end-of-input
  4. framework loops read() to fill a chunk
  5. reader is stateful → cursor/position

basics

~10 s

ItemReader is the input side of a chunk-oriented step. Its single method read() returns one item at a time, or null when there is no more data, signalling the end of input.

solid answer

~40 s

ItemReader<T> is the read side of Spring Batch's chunk-oriented processing. It has one method, T read(), which returns exactly one item per call or null to signal that input is exhausted. The step framework calls read() repeatedly to fill a chunk (up to the configured chunk/commit-interval size), then hands the chunk to the ItemProcessor and ItemWriter inside a transaction. Because read() is one-at-a-time, a reader is inherently stateful: it must remember its position between calls. Returning null ends the step's reading loop. read() may throw a checked Exception; a thrown exception is not the same as null — null means 'clean end of data', an exception means 'error' and triggers skip/retry/fail handling.

code

java · 13 lines
java
public class CountingItemReader implements ItemReader<Integer> {
    private final Iterator<Integer> it;

    public CountingItemReader(List<Integer> data) {
        this.it = data.iterator();
    }

    @Override
    public Integer read() {
        // one item per call; null when exhausted
        return it.hasNext() ? it.next() : null;
    }
}

go deeper

for a junior

Must know: read() returns one item, null ends input.

for a middle

Should connect read() to the chunk loop and commit-interval.

for a senior

Should mention statefulness, ItemStream, thread-safety implications.

for a principal

Frames read() semantics against restartability and transaction boundaries.

### The interface ```java public interface ItemReader<T> { T read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException; } ``` `ItemReader<T>` is the **input** abstraction in Spring Batch's **chunk-oriented** processing model. A chunk-oriented `Step` runs a loop: 1. Call `read()` up to *N* times, where *N* is the **chunk size** (also called **commit-interval**), collecting items into a list. 2. Pass each item through the optional `ItemProcessor`. 3. Hand the whole list to the `ItemWriter.write(...)`. 4. Commit the surrounding transaction, update the `ExecutionContext`, and repeat. ### The read() contract - **One item at a time.** Each call returns a single `T`. The framework — not your code — decides how many times to call it before flushing a chunk. - **`null` means end-of-input.** When there is no more data, `read()` returns `null`. This is the *only* way to signal a clean end; the step's read loop stops and the step completes. - **`null` is a sentinel, not a data value.** You cannot have a legitimate `null` element in your stream — it always means 'exhausted'. - **Exceptions mean error, not end.** Throwing (e.g. `ParseException`, `FlatFileParseException`, `NonTransientResourceException`) triggers the step's skip / retry / fail policies. Never throw to mean 'done'. ### Statefulness Because reading is incremental, a reader keeps a **cursor/position** internally (current line number, ResultSet position, page number, XML fragment index). This is why most real readers also implement `ItemStream` (open/update/close) so their position can be saved to and restored from the `ExecutionContext` for **restart**. ### Built-in implementations Spring Batch ships many: `FlatFileItemReader` (CSV/fixed-width), `JdbcCursorItemReader` / `JdbcPagingItemReader` (JDBC), `JpaCursorItemReader` / `JpaPagingItemReader` (JPA), `StaxEventItemReader` (XML), `KafkaItemReader`, `MongoPagingItemReader`, etc. You rarely implement `ItemReader` by hand; you configure a provided one. ### Gotchas - Returning the same item forever (never returning `null`) causes an **infinite step**. - A reader is generally **not thread-safe**; in multi-threaded steps you must synchronize (e.g. `SynchronizedItemStreamReader`) or use a partitioned/cursor-safe reader. - `read()` is called *inside* the chunk transaction boundary in most configurations, so slow reads hold the transaction open.

  • What happens if read() never returns null?
    The step's read loop never terminates, so the step runs forever (or until it exhausts memory/times out). null is the only clean stop signal.
  • How does throwing an exception from read() differ from returning null?
    null = clean end of data, step completes normally. An exception triggers the step's error handling (skip, retry, or fail) depending on the fault-tolerant configuration; it never means 'done'.

context

open as a page

Explain the ItemStream interface (open/update/close) and how it enables restart of a failed Spring Batch step.

level: seniorimportance: must knowfreq 68%

basics

~20 s

ItemStream lets a reader/writer save and restore its position. open() initializes or restores state from the ExecutionContext, update() periodically writes the current position into it (at each chunk commit), and close() releases resources. On restart, open() reads the saved state so the step resumes where it stopped.

open as a page

How do you configure a FlatFileItemReader to parse a delimited CSV file into domain objects?

level: middleimportance: should knowfreq 70%

basics

~20 s

FlatFileItemReader reads a text file line by line. You give it a Resource, a LineMapper that splits each line (a DelimitedLineTokenizer into a FieldSet) and maps the fields to an object (a FieldSetMapper), plus optional linesToSkip for headers.

open as a page

When reading from a database in Spring Batch, when would you choose a cursor-based reader (JdbcCursorItemReader) over a paging reader (JpaPagingItemReader), and what are the trade-offs?

level: seniorimportance: should knowfreq 65%

basics

~20 s

Cursor readers stream one big result set over a single open connection/ResultSet — low memory, one query, but the connection stays open and it is not restartable across threads. Paging readers run repeated LIMIT/OFFSET queries per page — connection released between pages, restartable and partition-friendly, but more queries and needs a stable sort.

open as a page

You need to read a large XML file with StaxEventItemReader inside a multi-threaded step, and guarantee correct restart. What issues arise and how do you address them?

level: principalimportance: nice to knowfreq 40%

basics

~20 s

StaxEventItemReader streams XML fragments via StAX and is stateful, so it is not thread-safe and its restart state can't be safely shared across threads. Either keep the step single-threaded, wrap it in SynchronizedItemStreamReader, or partition the input; be aware synchronizing serializes reads and can make saved state inconsistent.

open as a page