Buffering
Use buffered I/O for file-based readers to minimize system calls
Ce contenu n’est pas encore disponible dans votre langue.
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:
| Reader | Feature Flag | Data Source | Description |
|---|---|---|---|
CsvItemReader<R> | csv | CSV files/strings | Reads CSV records with configurable delimiters |
JsonItemReader<I, R> | json | JSON arrays | Streaming JSON array parser |
XmlItemReader<R, I> | xml | XML documents | Reads XML elements by tag name |
PostgresRdbcItemReader<I> | rdbc-postgres | PostgreSQL | Paginated PostgreSQL queries |
MysqlRdbcItemReader<I> | rdbc-mysql | MySQL/MariaDB | Paginated MySQL queries |
SqliteRdbcItemReader<I> | rdbc-sqlite | SQLite | Paginated SQLite queries |
MongodbItemReader<I> | mongodb | MongoDB | Cursor-based MongoDB queries |
OrmItemReader<I> | orm | SeaORM | ORM-based database reading |
PersonReader | fake | Fake data | Generates fake person data for testing |
pub struct CsvItemReaderBuilder<R: Read> { /* ... */ }| Method | Type | Default | Description |
|---|---|---|---|
has_headers(bool) | bool | true | First row contains headers |
delimiter(u8) | u8 | b',' | Field delimiter character |
quote(u8) | u8 | b'"' | Quote character |
flexible(bool) | bool | false | Allow 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")?;let csv_data = "id,name,price\n1,Laptop,999.99\n2,Mouse,29.99";
let reader = CsvItemReaderBuilder::<Product>::new() .has_headers(true) .from_reader(csv_data.as_bytes());let reader = CsvItemReaderBuilder::<Product>::new() .has_headers(true) .delimiter(b';') // Semicolon-separated .from_path("data.csv")?;pub struct JsonItemReaderBuilder<I, R: Read> { /* ... */ }| Method | Type | Description |
|---|---|---|
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")?;let json_data = r#"[ {"id": 1, "name": "Alice", "email": "alice@example.com"}, {"id": 2, "name": "Bob", "email": "bob@example.com"}]"#;
let reader = JsonItemReaderBuilder::<User>::new() .from_reader(json_data.as_bytes());pub struct XmlItemReaderBuilder<R: Read, I> { /* ... */ }| Method | Type | Default | Description |
|---|---|---|---|
tag(&str) | &str | required | XML tag name to extract |
capacity(usize) | usize | 8192 | Buffer 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> { /* ... */ }| Method | Type | Default | Description |
|---|---|---|---|
postgres(PgPool) | PgPool | required | PostgreSQL connection pool |
query(&str) | &str | required | SQL SELECT query |
with_page_size(usize) | usize | 100 | Number 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> { /* ... */ }| Method | Type | Default | Description |
|---|---|---|---|
collection(&Collection<I>) | &Collection<I> | required | MongoDB collection |
filter(Document) | Document | doc! {} | Query filter |
page_size(u32) | u32 | 100 | Documents 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 entityuse 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 { /* ... */ }| Method | Type | Default | Description |
|---|---|---|---|
number_of_items(usize) | usize | required | How 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: Stringlast_name: Stringtitle: Stringemail: Stringcity: StringYou 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