spring-batch-rs 0.3.4

A toolkit for building enterprise-grade batch applications
Documentation
# Unified RDBC Builders

This document explains how to use the unified builder API for RDBC readers and writers.

## Overview

The Spring Batch RS library now provides unified builders that allow you to create database readers and writers with a consistent API across PostgreSQL, MySQL, and SQLite. This simplifies the code and makes it easier to switch between database types.

## Key Benefits

1. **Consistent API**: Same builder pattern for all database types
2. **Type Safety**: Compile-time validation of database-specific types
3. **Clear Intent**: Database type is explicitly specified in the builder
4. **Easy Migration**: Switch database types by changing a single method call

## RdbcItemReaderBuilder

The `RdbcItemReaderBuilder` provides a unified way to create database readers.

### PostgreSQL Example

```rust
use spring_batch_rs::item::rdbc::RdbcItemReaderBuilder;
use sqlx::PgPool;

#[derive(sqlx::FromRow, Clone)]
struct User {
    id: i32,
    name: String,
    email: String,
}

async fn create_postgres_reader() -> Result<(), Box<dyn std::error::Error>> {
    let pool = PgPool::connect("postgresql://user:pass@localhost/db").await?;

    let reader = RdbcItemReaderBuilder::<User>::new()
        .postgres(pool)                              // Specify PostgreSQL
        .query("SELECT id, name, email FROM users")  // SQL query
        .with_page_size(100)                         // Optional pagination
        .build_postgres();                           // Build PostgreSQL reader

    Ok(())
}
```

### MySQL Example

```rust
use spring_batch_rs::item::rdbc::RdbcItemReaderBuilder;
use sqlx::MySqlPool;

async fn create_mysql_reader() -> Result<(), Box<dyn std::error::Error>> {
    let pool = MySqlPool::connect("mysql://user:pass@localhost/db").await?;

    let reader = RdbcItemReaderBuilder::<User>::new()
        .mysql(pool)                                 // Specify MySQL
        .query("SELECT id, name, email FROM users")  // SQL query
        .with_page_size(100)                         // Optional pagination
        .build_mysql();                              // Build MySQL reader

    Ok(())
}
```

### SQLite Example

```rust
use spring_batch_rs::item::rdbc::RdbcItemReaderBuilder;
use sqlx::SqlitePool;

async fn create_sqlite_reader() -> Result<(), Box<dyn std::error::Error>> {
    let pool = SqlitePool::connect("sqlite::memory:").await?;

    let reader = RdbcItemReaderBuilder::<User>::new()
        .sqlite(pool)                                // Specify SQLite
        .query("SELECT id, name, email FROM users")  // SQL query
        .with_page_size(100)                         // Optional pagination
        .build_sqlite();                             // Build SQLite reader

    Ok(())
}
```

## RdbcItemWriterBuilder

The `RdbcItemWriterBuilder` provides a unified way to create database writers.

### PostgreSQL Example

```rust
use spring_batch_rs::item::rdbc::{RdbcItemWriterBuilder, DatabaseItemBinder};
use sqlx::{PgPool, query_builder::Separated, Postgres};

struct UserBinder;
impl DatabaseItemBinder<User, Postgres> for UserBinder {
    fn bind(&self, item: &User, mut query_builder: Separated<Postgres, &str>) {
        query_builder.push_bind(item.id);
        query_builder.push_bind(&item.name);
        query_builder.push_bind(&item.email);
    }
}

async fn create_postgres_writer() -> Result<(), Box<dyn std::error::Error>> {
    let pool = PgPool::connect("postgresql://user:pass@localhost/db").await?;
    let binder = UserBinder;

    let writer = RdbcItemWriterBuilder::<User>::new()
        .postgres(&pool)           // Specify PostgreSQL
        .table("users")            // Target table
        .add_column("id")          // Column names
        .add_column("name")
        .add_column("email")
        .postgres_binder(&binder)  // Database-specific binder
        .build_postgres();         // Build PostgreSQL writer

    Ok(())
}
```

### MySQL Example

```rust
use spring_batch_rs::item::rdbc::{RdbcItemWriterBuilder, DatabaseItemBinder};
use sqlx::{MySqlPool, query_builder::Separated, MySql};

struct UserBinder;
impl DatabaseItemBinder<User, MySql> for UserBinder {
    fn bind(&self, item: &User, mut query_builder: Separated<MySql, &str>) {
        query_builder.push_bind(item.id);
        query_builder.push_bind(&item.name);
        query_builder.push_bind(&item.email);
    }
}

async fn create_mysql_writer() -> Result<(), Box<dyn std::error::Error>> {
    let pool = MySqlPool::connect("mysql://user:pass@localhost/db").await?;
    let binder = UserBinder;

    let writer = RdbcItemWriterBuilder::<User>::new()
        .mysql(&pool)              // Specify MySQL
        .table("users")            // Target table
        .add_column("id")          // Column names
        .add_column("name")
        .add_column("email")
        .mysql_binder(&binder)     // Database-specific binder
        .build_mysql();            // Build MySQL writer

    Ok(())
}
```

### SQLite Example

```rust
use spring_batch_rs::item::rdbc::{RdbcItemWriterBuilder, DatabaseItemBinder};
use sqlx::{SqlitePool, query_builder::Separated, Sqlite};

struct UserBinder;
impl DatabaseItemBinder<User, Sqlite> for UserBinder {
    fn bind(&self, item: &User, mut query_builder: Separated<Sqlite, &str>) {
        query_builder.push_bind(item.id);
        query_builder.push_bind(&item.name);
        query_builder.push_bind(&item.email);
    }
}

async fn create_sqlite_writer() -> Result<(), Box<dyn std::error::Error>> {
    let pool = SqlitePool::connect("sqlite::memory:").await?;
    let binder = UserBinder;

    let writer = RdbcItemWriterBuilder::<User>::new()
        .sqlite(&pool)             // Specify SQLite
        .table("users")            // Target table
        .add_column("id")          // Column names
        .add_column("name")
        .add_column("email")
        .sqlite_binder(&binder)    // Database-specific binder
        .build_sqlite();           // Build SQLite writer

    Ok(())
}
```

## Migration from Database-Specific Builders

### Before (Database-Specific Builder)

```rust
// PostgreSQL-specific
use spring_batch_rs::item::rdbc::PostgresRdbcItemReaderBuilder;

let reader = PostgresRdbcItemReaderBuilder::<User>::new()
    .pool(pg_pool)
    .query("SELECT * FROM users")
    .with_page_size(100)
    .build();
```

### After (Unified Builder)

```rust
// Unified builder with explicit database type
use spring_batch_rs::item::rdbc::RdbcItemReaderBuilder;

let reader = RdbcItemReaderBuilder::<User>::new()
    .postgres(pg_pool)              // Database type specified here
    .query("SELECT * FROM users")
    .with_page_size(100)
    .build_postgres();              // And here
```

## Key Differences

1. **Database Type Parameter**: The unified builder uses method chaining to specify the database type (`.postgres()`, `.mysql()`, `.sqlite()`)
2. **Build Method**: Each database has its own build method (`.build_postgres()`, `.build_mysql()`, `.build_sqlite()`)
3. **Binder Methods**: Writers use database-specific binder methods (`.postgres_binder()`, `.mysql_binder()`, `.sqlite_binder()`)

## Advantages Over Database-Specific Builders

1. **Consistency**: Same API pattern across all databases
2. **Discoverability**: All database options available from single import
3. **Maintainability**: Easier to switch databases without changing entire code structure
4. **Clarity**: Database type is explicit in the builder chain

## When to Use Each Approach

### Use Unified Builders When:
- Building applications that might support multiple databases
- Creating reusable components that work across database types
- Preferring consistent API patterns

### Use Database-Specific Builders When:
- Working exclusively with one database type
- Needing database-specific optimizations
- Preferring specialized type names

Both approaches are fully supported and produce identical results. The unified builders are built on top of the database-specific builders and provide an alternative, more consistent API.