Start Small
Begin with small chunk sizes (10-100) and adjust based on your data and memory constraints
Ce contenu n’est pas encore disponible dans votre langue.
Welcome to Spring Batch RS! This guide will walk you through creating your first batch processing application in Rust.
Before you begin, ensure you have:
cargo new my-batch-appcd my-batch-appAdd Spring Batch RS to your Cargo.toml:
[dependencies]spring-batch-rs = { version = "0.3", features = ["csv", "json"] }serde = { version = "1.0", features = ["derive"] }Create a simple CSV to JSON converter:
use spring_batch_rs::{ core::{job::JobBuilder, step::StepBuilder, item::PassThroughProcessor}, item::{csv::CsvItemReaderBuilder, json::JsonItemWriterBuilder}, BatchError,};use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]struct Product { id: u32, name: String, price: f64, category: String,}
fn main() -> Result<(), BatchError> { // Sample CSV data let csv_data = r#"id,name,price,category1,Laptop,999.99,Electronics2,Coffee Mug,12.99,Kitchen3,Notebook,5.99,Office4,Wireless Mouse,29.99,Electronics"#;
// Create CSV reader let reader = CsvItemReaderBuilder::<Product>::new() .has_headers(true) .from_reader(csv_data.as_bytes());
// Create JSON writer let writer = JsonItemWriterBuilder::<Product>::new() .pretty_formatter(true) .from_path("products.json");
// Create processor (pass-through in this case) let processor = PassThroughProcessor::<Product>::new();
// Build the step let step = StepBuilder::new("csv-to-json-step") .chunk(10) .reader(&reader) .processor(&processor) .writer(&writer) .build();
// Build and run the job let job = JobBuilder::new() .start(&step) .build();
// Execute the job let result = job.run()?;
println!("✅ Job completed successfully!"); println!("📊 Processed {} steps", result.get_step_executions().len());
Ok(())}cargo runYou should see:
✅ Job completed successfully!📊 Processed 1 stepsAnd a products.json file with your converted data!
A Job is the top-level container for your entire batch process. It’s composed of one or more Steps that execute sequentially.
let job = JobBuilder::new() .start(&step1) // First step .next(&step2) // Second step (optional) .next(&step3) // Third step (optional) .build();A Step represents an independent phase of processing. There are two types:
Process large datasets in configurable chunks using the read-process-write pattern:
let step = StepBuilder::new("process-data") .chunk(100) // Process 100 items at a time .reader(&reader) // Read data source .processor(&processor) // Transform items .writer(&writer) // Write results .build();Execute a single task or operation:
let step = StepBuilder::new("cleanup") .tasklet(&cleanup_tasklet) .build();An ItemReader retrieves input data one item at a time from various sources.
use spring_batch_rs::item::csv::CsvItemReaderBuilder;
let reader = CsvItemReaderBuilder::<Product>::new() .has_headers(true) .delimiter(b',') .from_path("products.csv")?;use spring_batch_rs::item::json::JsonItemReaderBuilder;
let reader = JsonItemReaderBuilder::<Product>::new() .from_path("products.json")?;use spring_batch_rs::item::orm::OrmItemReaderBuilder;use sea_orm::{Database, EntityTrait};
let db = Database::connect("sqlite::memory:").await?;let query = ProductEntity::find();
let reader = OrmItemReaderBuilder::new() .connection(&db) .query(query) .page_size(100) .build();An ItemProcessor applies business logic to transform or filter items.
use spring_batch_rs::core::item::ItemProcessor;
struct PriceDiscountProcessor { discount_rate: f64,}
impl ItemProcessor<Product, Product> for PriceDiscountProcessor { fn process(&self, item: Product) -> Result<Option<Product>, BatchError> { let mut product = item;
// Apply discount product.price *= (1.0 - self.discount_rate);
// Filter out items below minimum price if product.price < 5.0 { return Ok(None); // Skip this item }
Ok(Some(product)) }}
// Usagelet processor = PriceDiscountProcessor { discount_rate: 0.15 };An ItemWriter outputs processed items to various destinations.
use spring_batch_rs::item::json::JsonItemWriterBuilder;
// Replace MyType with your actual output typelet writer = JsonItemWriterBuilder::<MyType>::new() .pretty_formatter(true) .from_path("output.json");use spring_batch_rs::item::csv::CsvItemWriterBuilder;
let writer = CsvItemWriterBuilder::new() .has_headers(true) .delimiter(b',') .from_path("output.csv")?;use spring_batch_rs::item::orm::OrmItemWriterBuilder;
let writer = OrmItemWriterBuilder::new() .connection(&db) .build();Let’s build a more sophisticated batch job that:
use spring_batch_rs::{ core::{job::JobBuilder, step::StepBuilder, item::ItemProcessor}, item::{csv::CsvItemReaderBuilder, json::JsonItemWriterBuilder}, BatchError,};use serde::{Deserialize, Serialize};
#[derive(Deserialize, Clone)]struct RawProduct { id: u32, name: String, price: f64, category: String, stock: i32,}
#[derive(Serialize)]struct ProcessedProduct { id: u32, name: String, final_price: f64, category: String, stock_status: String, price_tier: String,}
struct ProductProcessor;
impl ItemProcessor<RawProduct, ProcessedProduct> for ProductProcessor { fn process(&self, item: RawProduct) -> Result<Option<ProcessedProduct>, BatchError> { // Validate price if item.price <= 0.0 { return Ok(None); // Skip invalid items }
// Apply category-specific discount let discount = match item.category.as_str() { "Electronics" => 0.15, "Kitchen" => 0.10, "Office" => 0.05, _ => 0.0, };
let final_price = item.price * (1.0 - discount);
// Determine stock status let stock_status = if item.stock == 0 { "Out of Stock" } else if item.stock < 10 { "Low Stock" } else { "In Stock" }.to_string();
// Classify price tier let price_tier = if final_price < 20.0 { "Budget" } else if final_price < 100.0 { "Mid-Range" } else { "Premium" }.to_string();
Ok(Some(ProcessedProduct { id: item.id, name: item.name, final_price, category: item.category, stock_status, price_tier, })) }}
fn main() -> Result<(), BatchError> { // Create input file with sample data let csv_data = r#"id,name,price,category,stock1,Laptop,999.99,Electronics,252,Coffee Mug,12.99,Kitchen,1503,Notebook,5.99,Office,84,Wireless Mouse,29.99,Electronics,05,Desk Lamp,45.00,Office,50"#;
let reader = CsvItemReaderBuilder::<RawProduct>::new() .has_headers(true) .from_reader(csv_data.as_bytes());
let writer = JsonItemWriterBuilder::<ProcessedProduct>::new() .pretty_formatter(true) .from_path("processed_products.json");
let processor = ProductProcessor;
let step = StepBuilder::new("process-products") .chunk(10) .reader(&reader) .processor(&processor) .writer(&writer) .build();
let job = JobBuilder::new() .start(&step) .build();
job.run()?;
println!("✅ Processing complete! Check processed_products.json");
Ok(())}Spring Batch RS provides robust error handling with configurable skip limits:
let step = StepBuilder::new("fault-tolerant-step") .chunk(100) .reader(&reader) .processor(&processor) .writer(&writer) .skip_limit(10) // Allow up to 10 errors before failing .build();Start Small
Begin with small chunk sizes (10-100) and adjust based on your data and memory constraints
Validate Early
Perform validation in your processor to catch errors before writing
Use Skip Limits Wisely
Set appropriate skip limits based on acceptable data quality thresholds
Log Errors
Implement custom error logging to track skipped items for later review
Now that you understand the basics, explore more advanced features:
Processing Models
Learn about chunk-oriented vs tasklet processing patterns
Item Readers & Writers
Explore all available data sources and destinations
Tasklets
Use tasklets for file operations, FTP transfers, and custom tasks
Examples
Browse comprehensive examples for every feature
Happy batch processing! 🚀