skip to content

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