📚 Docs 📖 API 🌊 Home

📝 Examples & Patterns

Basic CRUD Operations

Create a User

use tideorm::prelude::*;

#[tideorm::model]
#[tide(table = "users")]
pub struct User {
    #[tide(primary_key, auto_increment)]
    pub id: i64,
    pub email: String,
    pub name: String,
    pub active: bool,
}

// Create a new user
let user = User {
    id: 0,
    email: "john@example.com".to_string(),
    name: "John Doe".to_string(),
    active: true,
};
let user = user.save().await?;

Read Users

// Find by ID
let user = User::find(1).await?;

// Find all active users
let active_users = User::query()
    .where_eq("active", true)
    .get()
    .await?;

// Find with pagination
let (users, total) = User::query()
    .order_by("name", Order::Asc)
    .paginate(page: 1, per_page: 20)
    .await?;

Update User

let mut user = User::find(1).await?;
user.name = "Jane Doe".to_string();
let user = user.update().await?;

// Or batch update
User::query()
    .where_eq("active", false)
    .update()
    .set("active", true)
    .execute()
    .await?;

Delete User

let user = User::find(1).await?;
user.delete().await?;

// Or delete multiple
User::query()
    .where_lt("age", 18)
    .delete()
    .await?;

Error Handling Patterns

Handle Not Found

match User::find(999).await {
    Ok(user) => {
        println!("Found: {}", user.name);
    }
    Err(e) if e.is_not_found() => {
        eprintln!("User not found");
    }
    Err(e) if e.is_connection_error() => {
        eprintln!("Database connection failed, retrying...");
    }
    Err(e) => {
        eprintln!("Error: {}", e);
    }
}

Retry Logic for Transient Failures

async fn with_retries<F, T>(
    mut f: F, 
    max_retries: u32
) -> tideorm::Result<T>
where
    F: FnMut() -> Pin<Box<dyn Future<Output = tideorm::Result<T>>>>,
{
    let mut retries = 0;
    loop {
        match f().await {
            Ok(result) => return Ok(result),
            Err(e) if e.is_connection_error() && retries < max_retries => {
                retries += 1;
                tokio::time::sleep(
                    Duration::from_secs(2_u64.pow(retries))
                ).await;
                continue;
            }
            Err(e) => return Err(e),
        }
    }
}

// Usage
let user = with_retries(|| {
    Box::pin(User::find(1))
}, 3).await?;

Transaction Patterns

Basic Transaction

let db = db()?;
let txn = db.begin().await?;

match (
    user.save_in_txn(&txn).await,
    post.save_in_txn(&txn).await
) {
    (Ok(u), Ok(p)) => {
        txn.commit().await?;
        Ok((u, p))
    }
    _ => {
        txn.rollback().await?;
        Err(Error::transaction("Failed to create records"))
    }
}

Nested Transactions

let db = db()?;
let txn = db.begin().await?;

// Savepoint 1
let sp1 = txn.savepoint().await?;
user1.save_in_txn(&txn).await?;

// Savepoint 2
let sp2 = txn.savepoint().await?;
user2.save_in_txn(&txn).await?;

if should_rollback_user2 {
    sp2.rollback().await?;
}

txn.commit().await?;

Relations & Eager Loading

Define Relations

#[tideorm::model]
#[tide(table = "posts")]
pub struct Post {
    #[tide(primary_key, auto_increment)]
    pub id: i64,
    
    #[belongs_to(User)]
    pub user_id: i64,
    
    pub title: String,
    pub content: String,
    
    // Relations (not stored in DB)
    #[tide(skip)]
    pub user: Option<User>,
}

#[tideorm::model]
#[tide(table = "users")]
pub struct User {
    #[tide(primary_key, auto_increment)]
    pub id: i64,
    pub name: String,
    
    // Has many
    #[tide(skip)]
    pub posts: Vec<Post>,
}

Eager Loading Relations

// Load posts with authors
let posts = Post::with_relations(["user"])
    .get()
    .await?;

// Access loaded relation
for post in posts {
    if let Some(user) = post.user {
        println!("Post by: {}", user.name);
    }
}

Batch Operations

Batch Insert

let users = vec![
    User {
        id: 0,
        email: "user1@example.com".to_string(),
        name: "User 1".to_string(),
        active: true,
    },
    User {
        id: 0,
        email: "user2@example.com".to_string(),
        name: "User 2".to_string(),
        active: true,
    },
];

// Single query instead of N queries
User::insert_all(&users).await?;

Batch Update

User::query()
    .where_eq("active", false)
    .where_lt("last_login", one_year_ago)
    .batch_update(&[
        UpdateValue::new("active", false),
        UpdateValue::new("archived", true),
    ])
    .execute()
    .await?;

Performance Optimization

Query Profiling

let profiler = Profiler::start();

let users = User::all().await?;
let posts = Post::all().await?;

let report = profiler.stop();
println!("{}", report);

// Output:
// Total queries: 2
// Total time: 45ms
// Slowest: SELECT * FROM posts (30ms)

Prepared Statement Caching

// Automatically cached for repeated queries
for user_id in user_ids {
    let user = User::find(user_id).await?;  // Reuses prepared statement
    process(&user);
}

// Check cache stats
let stats = PreparedStatementCache::global().stats();
println!("Prepared statements: {}", stats.total);
println!("Cache hit rate: {:.2}%", stats.hit_rate);

Query Caching

let cache_config = CacheConfig::new()
    .strategy(CacheStrategy::LRU)
    .max_entries(1000)
    .ttl_seconds(3600);

// Cache the result
let active_users = User::query()
    .where_eq("active", true)
    .cache(&cache_config)
    .get()
    .await?;

// Invalidate when needed
QueryCache::global().invalidate_pattern("users:*");

Soft Deletes

Define Soft Deletable Model

#[tideorm::model]
#[tide(table = "users", soft_delete)]
pub struct User {
    #[tide(primary_key, auto_increment)]
    pub id: i64,
    pub email: String,
    pub name: String,
    pub deleted_at: Option<DateTime<Utc>>,
}

Using Soft Deletes

// Soft delete (sets deleted_at)
let user = user.soft_delete().await?;

// Query excludes soft-deleted by default
let active_users = User::query()
    .where_eq("active", true)
    .get()
    .await?;  // Soft-deleted excluded automatically

// Include soft-deleted records
let all_users = User::query()
    .where_eq("active", true)
    .with_soft_deleted()
    .get()
    .await?;  // Includes soft-deleted

Concurrent Operations

Parallel Queries

// Execute multiple queries concurrently
let (users, posts, comments) = tokio::try_join!(
    User::query().get(),
    Post::query().get(),
    Comment::query().get(),
)?;

println!("Users: {}", users.len());
println!("Posts: {}", posts.len());
println!("Comments: {}", comments.len());

Concurrent Inserts

let handles: Vec<_> = user_ids
    .into_iter()
    .map(|id| {
        tokio::spawn(async move {
            User::find(id).await
        })
    })
    .collect();

let results = futures::future::try_join_all(handles).await?;

Common Patterns

✓ DO: Use global database reference

// Initialize once
TideConfig::init()
    .database(url)
    .connect()
    .await?;

// Use anywhere in your app
let user = User::find(1).await?;

✗ DON'T: Create multiple pools

// Bad - creates new pool each time
for item in items {
    TideConfig::init()  // WRONG!
        .database(url)
        .connect()
        .await?;
}