Skip to content

ItemReader API

The ItemReader<I> trait defines how to read items one at a time from any data source. It’s the entry point of the chunk-oriented processing model.

pub trait ItemReader<I> {
/// Reads the next item from the source
///
/// # Returns
/// - `Ok(Some(item))` - Successfully read an item
/// - `Ok(None)` - End of data reached
/// - `Err(BatchError)` - An error occurred
fn read(&self) -> Result<Option<I>, BatchError>;
}
pub type ItemReaderResult<I> = Result<Option<I>, BatchError>;

Spring Batch RS provides 9 built-in reader implementations:

ReaderFeature FlagData SourceDescription
CsvItemReader<R>csvCSV files/stringsReads CSV records with configurable delimiters
JsonItemReader<I, R>jsonJSON arraysStreaming JSON array parser
XmlItemReader<R, I>xmlXML documentsReads XML elements by tag name
PostgresRdbcItemReader<I>rdbc-postgresPostgreSQLPaginated PostgreSQL queries
MysqlRdbcItemReader<I>rdbc-mysqlMySQL/MariaDBPaginated MySQL queries
SqliteRdbcItemReader<I>rdbc-sqliteSQLitePaginated SQLite queries
MongodbItemReader<I>mongodbMongoDBCursor-based MongoDB queries
OrmItemReader<I>ormSeaORMORM-based database reading
PersonReaderfakeFake dataGenerates fake person data for testing

pub struct CsvItemReaderBuilder<R: Read> { /* ... */ }
MethodTypeDefaultDescription
has_headers(bool)booltrueFirst row contains headers
delimiter(u8)u8b','Field delimiter character
quote(u8)u8b'"'Quote character
flexible(bool)boolfalseAllow variable fields per row
from_path(&str)--Read from file path
from_reader(R)--Read from any Read source
use spring_batch_rs::item::csv::CsvItemReaderBuilder;
use serde::Deserialize;
#[derive(Deserialize, Clone)]
struct Product {
id: u32,
name: String,
price: f64,
}
let reader = CsvItemReaderBuilder::<Product>::new()
.has_headers(true)
.delimiter(b',')
.from_path("products.csv")?;

pub struct JsonItemReaderBuilder<I, R: Read> { /* ... */ }
MethodTypeDescription
from_path(&str)-Read from file path
from_reader(R)-Read from any Read source
use spring_batch_rs::item::json::JsonItemReaderBuilder;
use serde::Deserialize;
#[derive(Deserialize, Clone)]
struct User {
id: u32,
name: String,
email: String,
}
let reader = JsonItemReaderBuilder::<User>::new()
.from_path("users.json")?;

pub struct XmlItemReaderBuilder<R: Read, I> { /* ... */ }
MethodTypeDefaultDescription
tag(&str)&strrequiredXML tag name to extract
capacity(usize)usize8192Buffer capacity in bytes
from_path(&str)--Read from file path
from_reader(R)--Read from any Read source
use spring_batch_rs::item::xml::XmlItemReaderBuilder;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename = "vehicle")]
struct Vehicle {
#[serde(rename = "@type")]
vehicle_type: String,
#[serde(rename = "@id")]
id: String,
make: String,
model: String,
year: i32,
}
let reader = XmlItemReaderBuilder::<Vehicle>::new()
.tag("vehicle") // Extract <vehicle> elements
.capacity(1024)
.from_path("vehicles.xml")?;

pub struct RdbcItemReaderBuilder<I> { /* ... */ }
MethodTypeDefaultDescription
postgres(PgPool)PgPoolrequiredPostgreSQL connection pool
query(&str)&strrequiredSQL SELECT query
with_page_size(usize)usize100Number of rows per page
build_postgres()--Build PostgreSQL reader
use spring_batch_rs::item::rdbc::RdbcItemReaderBuilder;
use sqlx::{PgPool, FromRow};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize, FromRow)]
struct Person {
id: i64,
first_name: String,
last_name: String,
email: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let pool = PgPool::connect("postgres://user:pass@localhost/db").await?;
let reader = RdbcItemReaderBuilder::<Person>::new()
.postgres(pool)
.query("SELECT id, first_name, last_name, email FROM persons WHERE active = true")
.with_page_size(50)
.build_postgres();
Ok(())
}

Same as PostgreSQL reader, but use mysql(MySqlPool) and build_mysql():

let reader = RdbcItemReaderBuilder::<Person>::new()
.mysql(pool)
.query("SELECT * FROM persons")
.with_page_size(50)
.build_mysql();

Same as PostgreSQL reader, but use sqlite(SqlitePool) and build_sqlite():

let reader = RdbcItemReaderBuilder::<Person>::new()
.sqlite(pool)
.query("SELECT * FROM persons")
.with_page_size(50)
.build_sqlite();

pub struct MongodbItemReaderBuilder<I> { /* ... */ }
MethodTypeDefaultDescription
collection(&Collection<I>)&Collection<I>requiredMongoDB collection
filter(Document)Documentdoc! {}Query filter
page_size(u32)u32100Documents per batch
use spring_batch_rs::item::mongodb::MongodbItemReaderBuilder;
use mongodb::{bson::doc, sync::Client};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Clone)]
struct Book {
title: String,
author: String,
isbn: String,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::with_uri_str("mongodb://localhost:27017")?;
let db = client.database("library");
let collection = db.collection::<Book>("books");
let filter = doc! { "author": "J.K. Rowling" };
let reader = MongodbItemReaderBuilder::new()
.collection(&collection)
.filter(filter)
.page_size(20)
.build();
Ok(())
}

Reads entities using SeaORM. Your entity must implement SeaORM’s EntityTrait.

use spring_batch_rs::item::orm::OrmItemReaderBuilder;
use sea_orm::{Database, DatabaseConnection};
// Assuming you have a SeaORM entity
use entity::person::Entity as PersonEntity;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let db: DatabaseConnection = Database::connect("sqlite::memory:").await?;
let reader = OrmItemReaderBuilder::new()
.entity(PersonEntity)
.connection(&db)
.page_size(50)
.build();
Ok(())
}

Generates fake person data for testing purposes.

pub struct PersonReaderBuilder { /* ... */ }
MethodTypeDefaultDescription
number_of_items(usize)usizerequiredHow many persons to generate
use spring_batch_rs::item::fake::person_reader::{PersonReaderBuilder, Person};
let reader = PersonReaderBuilder::new()
.number_of_items(1000)
.build();

The Person struct includes:

  • first_name: String
  • last_name: String
  • title: String
  • email: String
  • city: String

You can implement ItemReader for any data source:

use spring_batch_rs::core::item::{ItemReader, ItemReaderResult};
use spring_batch_rs::error::BatchError;
use std::sync::Mutex;
struct MyCustomReader {
data: Mutex<Vec<String>>,
}
impl ItemReader<String> for MyCustomReader {
fn read(&self) -> ItemReaderResult<String> {
let mut data = self.data.lock().unwrap();
if data.is_empty() {
Ok(None) // End of data
} else {
Ok(Some(data.remove(0))) // Return next item
}
}
}
impl MyCustomReader {
fn new(items: Vec<String>) -> Self {
Self {
data: Mutex::new(items),
}
}
}

Buffering

Use buffered I/O for file-based readers to minimize system calls

Pagination

Database readers should use pagination to avoid loading entire datasets into memory

Error Handling

Return descriptive BatchError::ItemReader errors with context about what failed

State Management

Use interior mutability (Mutex, RefCell) for stateful readers since read() takes &self