📝 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?;
}