akita

Crates.ioakita
lib.rsakita
version0.6.2
created_at2021-07-10 13:39:53.95315+00
updated_at2026-01-08 03:49:41.489213+00
descriptionAkita - Mini orm for rust.
homepagehttps://github.com/wslongchen/akita
repositoryhttps://github.com/wslongchen/akita
max_upload_size
id421102
size1,993,732
ๆฝ˜ๅฎ‰ (wslongchen)

documentation

https://docs.rs/akita

README

Akita

Akita Logo

A lightweight, fast and easy-to-use ORM framework for Rust

Crates.io Documentation Minimum Rust License Apache 2.0 License MIT

๐ŸŽฏ Features

  • ๐Ÿš€ High Performance: Pure Rust implementation, zero runtime overhead
  • ๐ŸŽฏ Easy to Use: Intuitive API, quick to learn
  • ๐Ÿ”ง Flexible Query: Powerful query builder with type safety
  • ๐Ÿ“ฆ Multi-Database: Native support for MySQL, PostgreSQL, SQLite, and any MySQL-compatible databases (TiDB, MariaDB, etc.)
  • ๐Ÿ”Œ Dual Runtime: Both synchronous and asynchronous operation modes
  • ๐Ÿ›ก๏ธ Type Safe: Full Rust type system support with compile-time checking
  • ๐Ÿ”„ Transaction: Complete ACID transaction management with savepoint support
  • โšก Connection Pool: Built-in high-performance connection pooling
  • ๐ŸŽจ Annotation Driven: Simplify entity definition with derive macros
  • ๐Ÿ”Œ Interceptors: Extensible interceptor system for AOP (Aspect-Oriented Programming)
  • ๐Ÿ“Š Pagination: Built-in smart pagination with total count
  • ๐Ÿ” Complex Query: Support for joins, subqueries, and complex SQL operations
  • ๐Ÿ› ๏ธ Raw SQL: Direct SQL execution when needed

๐Ÿ“ฆ Installation

Add this to your Cargo.toml:

For MySQL (Synchronous)

[dependencies]
akita = { version = "0.6", features = ["mysql-sync"] }

For MySQL (Asynchronous)

[dependencies]
akita = { version = "0.6", features = ["mysql-async"] }

For PostgreSQL:

[dependencies]
akita = { version = "0.6", features = ["postgres-sync"] }

For Oracle:

[dependencies]
akita = { version = "0.6", features = ["oracle-sync"] }

SqlServer:

[dependencies]
akita = { version = "0.6", features = ["mssql-sync"] }

For SQLite:

[dependencies]
akita = { version = "0.6", features = ["sqlite-sync"] }

For TiDB and MySQL-compatible Databases:

TiDB, MariaDB, and other MySQL-compatible databases can use the MySQL features:

[dependencies]
akita = { version = "0.6", features = ["mysql-sync"] }  # or "mysql-async"
chrono = "0.4"

๐Ÿš€ Quick Start

1. Define Your Entity

use akita::*;
use chrono::{NaiveDate, NaiveDateTime};
use serde_json::Value;

#[derive(Entity, Clone, Default, Debug)]
#[table(name = "users")]
pub struct User {
    #[id(name = "id")]
    pub id: i64,
    
    #[field(name = "user_name")]
    pub username: String,
    
    pub email: String,
    
    pub age: Option<u8>,
    
    #[field(name = "is_active")]
    pub active: bool,
    
    pub level: u8,
    
    pub metadata: Option<Value>,
    
    pub birthday: Option<NaiveDate>,
    
    pub created_at: Option<NaiveDateTime>,
    
    #[field(exist = "false")]
    pub full_name: String,
}

2. Initialize Akita

Synchronous Mode

use akita::prelude::*;
use std::time::Duration;

fn main() -> Result<()> {
    // Configuration for MySQL
    let cfg = AkitaConfig::new()
        .url("mysql://root:password@localhost:3306/mydb")
        .max_size(10)                     // Connection pool size
        .connection_timeout(Duration::from_secs(5));

    // Create Akita instance
    let akita = Akita::new(cfg)?;

    // For TiDB (uses MySQL protocol)
    let tidb_cfg = AkitaConfig::new()
        .url("mysql://root:@tidb-host:4000/mydb")
        .max_size(20);
    Ok(())
}

Asynchronous Mode

use akita::*;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<()> {
    // Async configuration
    let cfg = AkitaConfig::new()
        .url("mysql://root:password@localhost:3306/mydb")
        .max_size(10)
        .connection_timeout(Duration::from_secs(5));
    
    // Create async Akita instance
    let akita = Akita::new(cfg).await?;
    
    Ok(())
}

3. Basic Operations

Synchronous Operations

fn main() {
    // Create
    let user = User {
        username: "john_doe".to_string(),
        email: "john@example.com".to_string(),
        active: true,
        level: 1,
        ..Default::default()
    };

    let user_id: Option<i64> = akita.save(&user)?;

    // Read
    let user: Option<User> = akita.select_by_id(user_id.unwrap())?;

    // Update
    let mut user = user.unwrap();
    user.level = 2;
    akita.update_by_id(&user)?;

    // Delete
    akita.remove_by_id::<User, _>(user_id.unwrap())?;
}

Asynchronous Operations

fn main() {
    // Create
    let user = User {
        username: "jane_doe".to_string(),
        email: "jane@example.com".to_string(),
        active: true,
        level: 1,
        ..Default::default()
    };

    let user_id: Option<i64> = akita.save(&user).await?;

    // Read
    let user: Option<User> = akita.select_by_id(user_id.unwrap()).await?;

    // Update
    let mut user = user.unwrap();
    user.level = 2;
    akita.update_by_id(&user).await?;

    // Delete
    akita.remove_by_id::<User, _>(user_id.unwrap()).await?;
}

โฌ†๏ธDatabase Compatibility Matrix

Database Sync Feature Async Feature Protocol Sync Implementation Async Implementation Status Notes
MySQL mysql-sync mysql-async MySQL mysql crate mysql_async crate โœ… Production Ready Native Rust implementations
PostgreSQL postgres-sync postgres-async PostgreSQL tokio-postgres (blocking) tokio-postgres (async) โœ… Production Ready Both use tokio-postgres under the hood
SQLite sqlite-sync sqlite-async SQLite rusqlite crate sqlx with async runtime โœ… Production Ready Different implementation strategies
Oracle oracle-sync oracle-async Oracle oracle crate (blocking) oracle crate + async runtime โœ… Production Ready Oracle driver with async wrapper
SQL Server sqlserver-sync sqlserver-async TDS tiberius (blocking) tiberius (async) โœ… Production Ready Tiberius driver support
TiDB mysql-sync mysql-async MySQL Same as MySQL Same as MySQL โœ… Production Ready 100% MySQL compatible
MariaDB mysql-sync mysql-async MySQL Same as MySQL Same as MySQL โœ… Production Ready 100% MySQL compatible
OceanBase mysql-sync mysql-async MySQL Same as MySQL Same as MySQL โœ… Production Ready MySQL compatible mode

Implementation Details Summary

PostgreSQL Implementation

  • Sync: Uses tokio-postgres with blocking wrapper
  • Async: Direct tokio-postgres async client
  • Both share same underlying library

Oracle Implementation

  • Sync: Native oracle crate (synchronous driver)
  • Async: oracle crate wrapped with async runtime
  • Same driver, different execution model

SQLite Implementation

  • Sync: rusqlite crate (synchronous SQLite)
  • Async: sqlx with async SQLite support
  • Different libraries, same protocol

SQL Server Implementation

  • Sync: tiberius with blocking API
  • Async: tiberius native async API
  • Same library, different APIs

Feature Comparison

Feature MySQL/TiDB PostgreSQL SQLite Oracle SQL Server
ACID Transactions โœ… โœ… โœ… โœ… โœ…
Connection Pool โœ… โœ… โœ… โœ… โœ…
Native Async โœ… โœ… โš ๏ธ (via sqlx) โš ๏ธ (wrapped) โœ…
Sync via Async Runtime โŒ โœ… (blocking) โŒ โŒ โœ… (blocking)
JSON Support โœ… (JSON) โœ… (JSONB) โœ… (JSON1) โœ… โš ๏ธ (limited)
Full-text Search โœ… โœ… โœ… (FTS5) โœ… โœ…
Spatial Data โœ… โœ… โœ… (R*Tree) โœ… โœ…
Stored Procedures โœ… โœ… โŒ โœ… โœ…
Replication โœ… โœ… โŒ โœ… โœ…
Distributed TiDB โœ… โŒ โŒ โŒ โŒ
Protocol MySQL PostgreSQL SQLite Oracle TDS

Key Implementation Notes

  1. PostgreSQL: Both sync and async use tokio-postgres, sync is just a blocking wrapper

  2. Oracle: Sync is native driver, async is wrapper around same driver

  3. SQLite: Different libraries for sync (rusqlite) and async (sqlx)

  4. SQL Server: tiberius provides both sync (blocking) and async APIs

  5. MySQL: Separate sync (mysql) and async (mysql_async) crates

  6. TiDB/MariaDB/OceanBase: Use MySQL drivers with full compatibility

๐Ÿ“š Detailed Usage

Query Builder

Akita provides a powerful, type-safe query builder that works across all supported databases:

fn main() {
    use akita::*;

    // Database-agnostic query builder
    let wrapper = Wrapper::new()
        // Select specific columns
        .select(vec!["id", "username", "email"])

        // Universal conditions (works on all databases)
        .eq("status", 1)
        .ne("deleted", true)
        .gt("age", 18)
        .ge("score", 60)
        .lt("age", 65)
        .le("level", 10)

        // String operations
        .like("username", "%john%")
        .not_like("email", "%test%")

        // List operations
        .r#in("role", vec!["admin", "user"])
        .not_in("status", vec![0, 9])

        // Null checks
        .is_null("deleted_at")
        .is_not_null("created_at")

        // Between
        .between("age", 18, 65)
        .not_between("score", 0, 60)

        // Logical operations
        .and(|w| {
            w.eq("status", 1).or_direct().eq("status", 2)
        })
        .or(|w| {
            w.like("username", "%admin%").like("email", "%admin%")
        })

        // Ordering
        .order_by_asc(vec!["created_at"])
        .order_by_desc(vec!["id", "level"])

        // Grouping
        .group_by(vec!["department", "level"])

        // Having clause (database-specific optimizations)
        .having("COUNT(*)", SqlOperator::Gt, 1)

        // Pagination (optimized for each database)
        .limit(10)
        .offset(20);
}

Complex Queries with Database Optimizations

fn main() {
    // Join queries with database-specific optimizations
    let users: Vec<User> = akita.list(
        Wrapper::new()
            .eq("u.status", 1)
            .inner_join("departments d", "u.department_id = d.id")
            .select(vec!["u.*", "d.name as department_name"])
    )?;

    // Subqueries (automatically optimized for target database)
    let active_users: Vec<User> = akita.list(
        Wrapper::new()
            .r#in("id", |w| {
                w.select(vec!["user_id"])
                    .from("user_logs")
                    .eq("action", "login")
                    .gt("created_at", "2023-01-01")
            })
    )?;

    // Database-specific optimizations
    let query = Wrapper::new()
        .eq("status", 1)
        .order_by_desc(vec!["created_at"]);

    // For MySQL/TiDB: Uses LIMIT optimization
    // For PostgreSQL: Uses LIMIT OFFSET
    // For SQLite: Uses LIMIT OFFSET
    let result = akita.list::<User>(query.limit(100))?;
}

Database-Specific Features

MySQL/TiDB Specific Features

fn main() {
    // MySQL JSON functions
    let users: Vec<User> = akita.exec_raw(
        "SELECT * FROM users WHERE JSON_EXTRACT(metadata, '$.premium') = true",
        ()
    )?;

    // MySQL full-text search
    let users: Vec<User> = akita.list(
        Wrapper::new()
            .raw("MATCH(username, email) AGAINST(:search IN BOOLEAN MODE)")
            .set_param("search", "john*")
    )?;
}

PostgreSQL Specific Features

fn main() {
    // PostgreSQL JSONB operations
    let users: Vec<User> = akita.exec_raw(
        "SELECT * FROM users WHERE metadata @> '{\"premium\": true}'",
        ()
    )?;

    // PostgreSQL array operations
    let users: Vec<User> = akita.exec_raw(
        "SELECT * FROM users WHERE 'admin' = ANY(roles)",
        ()
    )?;
}

SQLite Specific Features

fn main() {
    // SQLite JSON1 extension
    let users: Vec<User> = akita.exec_raw(
        "SELECT * FROM users WHERE json_extract(metadata, '$.premium') = 1",
        ()
    )?;

    // SQLite full-text search (FTS5)
    let users: Vec<User> = akita.exec_raw(
        "SELECT * FROM users_fts WHERE users_fts MATCH 'john'",
        ()
    )?;
}

Raw SQL Queries with Database Portability

fn main() {
    // Parameterized queries
    let users: Vec<User> = akita.exec_raw(
        "SELECT * FROM users WHERE status = ? AND level > ?",
        (1, 0)
    )?;

    // Named parameters
    let user: Option<User> = akita.exec_first(
        "SELECT * FROM users WHERE username = :name AND email = :email",
        params! {
        "name" => "john",
        "email" => "john@example.com"
    }
    )?;

    // Executing DDL
    akita.exec_drop(
        "CREATE TABLE IF NOT EXISTS users (
        id BIGINT PRIMARY KEY AUTO_INCREMENT,
        username VARCHAR(50) NOT NULL,
        email VARCHAR(100) NOT NULL
    )",
        ()
    )?;
}

Transactions with Database-Specific Features

fn main() {
    // Simple transaction
    akita.start_transaction().and_then(|mut tx| {
        tx.save(&user1)?;
        tx.save(&user2)?;
        tx.update(&user3, wrapper)?;
        tx.commit()
    })?;

    // Nested transactions (savepoints)
    akita.start_transaction().and_then(|mut tx| {
        tx.save(&user1)?;


        match tx.save(&user2) {
            Ok(_) => {
                tx.commit()
            }
            Err(e) => {
                // Continue with other operations or rollback
                tx.rollback()
            }
        }
    })?;
}

Interceptors with Database Awareness

Akita supports powerful interceptor system that can adapt to different databases:

use akita::*;
use std::sync::Arc;
use std::time::Duration;

fn main() {
    // Create interceptor-enabled Akita
    let akita = Akita::new(config).unwrap()
    .with_interceptor_builder(
        InterceptorBuilder::new()
            .register(tenant_interceptor)
            .register(performance_interceptor)
            .register(logging_interceptor)
            .enable("trackable_tenant").unwrap()
            .enable("trackable_performance").unwrap()
            .enable("trackable_logging").unwrap()
    )?;
}

// Custom interceptor
#[derive(Debug)]
struct AuditInterceptor {
    user_id: String,
}

impl AkitaInterceptor for AuditInterceptor {
    fn name(&self) -> &'static str {
        "audit"
    }
    
    fn interceptor_type(&self) -> InterceptorType {
        InterceptorType::Audit
    }
    
    fn order(&self) -> i32 {
        50
    }
    
    fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
        // Add audit information to query
        ctx.set_metadata("audit_user", self.user_id.clone());
        ctx.set_metadata("audit_time", chrono::Utc::now().to_rfc3339());
        
        Ok(())
    }
}

Entity Methods with Database Portability

Entities can have their own methods:

impl User {
    // Custom finder methods
    pub fn find_active(akita: &Akita) -> Result<Vec<User>> {
        akita.list(Wrapper::new().eq("status", 1))
    }

    pub fn find_by_email(akita: &Akita, email: &str) -> Result<Option<User>> {
        akita.exec_first(
            "SELECT * FROM users WHERE email = ?",
            (email,)
        )
    }

    // Business logic methods
    pub fn promote(&mut self) {
        self.level += 1;
        // Add other business logic
    }

    pub fn is_vip(&self) -> bool {
        self.level >= 2
    }
}

fn main() {
    // Usage
    let active_users = User::find_active(&akita)?;
    let user = User::find_by_email(&akita, "john@example.com")?;
    let mut user = create_test_user();

    // Testing entity updates
    let result = user.update_by_id::<_>(&akita);
    assert!(result.is_ok(), "The entity update method should succeed");

    // Testing entity deletion
    let result = user.remove_by_id::<_,i32>(&akita, 1);
    assert!(result.is_ok(), "The entity deletion method should succeed");

    // Test the entity list query

    let result = User::list(&akita, Wrapper::new().eq("name", "Jack"));
    assert!(result.is_ok(), "The entity list query should succeed");

    // Testing entity paging queries
    let result = User::page::<_>(&akita, 1, 1, Wrapper::new().eq("name", "Jack"));
    assert!(result.is_ok(), "The entity paging query should succeed");
}

Pagination with Database Optimization

fn main() {
    // Simple pagination
    let page: IPage<User> = akita.page(1, 10, Wrapper::new().eq("status", 1))?;

    println!("Page {} of {}", page.current, page.size);
    println!("Total records: {}", page.total);
    println!("Records on this page: {}", page.records.len());

    // Complex pagination with custom ordering
    let _page = akita.page(
        1,
        10,
        Wrapper::new()
            .eq("department", "engineering")
            .ge("level", 3)
    )?;

    // Manual pagination
    let ipage = akita.page::<User>(
        1,
        10,
        Wrapper::new().eq("active", true)
    )?;
}

Batch Operations with Database Optimization

fn main() {
    // Batch insert
    let users = vec![
        User { username: "user1".to_string(), ..Default::default() },
        User { username: "user2".to_string(), ..Default::default() },
        User { username: "user3".to_string(), ..Default::default() },
    ];

    let _ = akita.save_batch(&users)?;

    // Batch update
    let mut users_to_update = vec![];
    for mut user in users {
        user.level += 1;
        users_to_update.push(user);
    }

    akita.update_batch_by_id(&users_to_update)?;

    // Batch delete
    akita.remove_by_ids::<User, _>(vec![1, 2, 3, 4, 5])?;
}

๐Ÿ”ง Configuration

AkitaConfig Options

fn main() {
    let config = AkitaConfig::new().url("mysql://root:password@localhost:3306/mydb")
        .max_size(20)                    // Maximum connection pool size
        .min_size(Some(5))                     // Minimum connection pool size
        .connection_timeout(Duration::from_secs(30))
        .idle_timeout(Duration::from_secs(300))
        .max_lifetime(Duration::from_secs(1800));
}

Environment-based Configuration

fn main() {
    use std::env;

    let database_url = env::var("DATABASE_URL")
        .unwrap_or_else(|_| "mysql://root:password@localhost:3306/mydb".to_string());

    let max_connections: u32 = env::var("DATABASE_MAX_CONNECTIONS")
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(10);

    let config = AkitaConfig::new().url(&database_url)
        .max_size(max_connections);
}

๐ŸŽจ Advanced Features

Custom Type Conversion

use akita::*;

#[derive(Debug, Clone)]
pub struct Email(String);

impl FromAkitaValue for Email {
    fn from_value(value: &AkitaValue) -> Result<Self, AkitaDataError> {
        match value {
            AkitaValue::Text(s) => Ok(Email(s.clone())),
            _ => Err(AkitaDataError::ConversionError(ConversionError::conversion_error(format!("Cannot convert {:?} to Email", value)))),
        }
    }
}

impl IntoAkitaValue for Email {
    fn into_value(&self) -> AkitaValue {
        AkitaValue::Text(self.0.clone())
    }
}

#[derive(Entity)]
pub struct UserWithEmail {
    pub id: i64,
    pub email: Email,  // Custom type
}

๐Ÿ“Š Performance Tips

  1. Connection Pooling: Always configure appropriate pool sizes
  • MySQL/TiDB: 10-100 connections based on workload

  • PostgreSQL: 5-50 connections

  • SQLite: 1 connection (file-based)

  1. Batch Operations: Use database-specific batch methods
  • MySQL: Multi-value INSERT statements

  • PostgreSQL: COPY command for large datasets

  • SQLite: Transactions around batch operations

  1. Query Optimization:
  • Use EXPLAIN on MySQL/PostgreSQL to analyze query plans

  • SQLite: Use appropriate indexes and avoid expensive operations in WHERE

  1. Statement Caching: Akita caches prepared statements automatically
  • Reduces parsing overhead on all databases

  • Especially beneficial for repeated queries

  1. Connection Reuse: Keep connections alive for related operations
  • Reduces connection establishment overhead

  • Maintains session state

  1. Database-Specific Features:
  • MySQL/TiDB: Use connection compression for remote connections

  • PostgreSQL: Use prepared statements for complex queries

  • SQLite: Enable WAL mode for better concurrency

๐Ÿค Contributing

We welcome contributions! Here's how you can help:

  1. Report Bugs: Create an issue with database-specific details

  2. Suggest Features: Start a discussion about new database support or features

  3. Submit PRs: Follow our contributing guide

  4. Improve Documentation: Help us make the docs better for all database backends

  5. Add Database Support: Implement support for new databases

Development Setup

# Clone the repository
git clone https://github.com/wslongchen/akita.git
cd akita

# Run tests for specific databases
cargo test --features mysql-sync
cargo test --features mysql-async
cargo test --features postgres-sync
cargo test --features sqlite-sync
cargo test --features oracle-sync

# Run all tests
cargo test --all-features

# Run examples
cargo run --example basic --features mysql-sync
cargo run --example async-basic --features mysql-async

# Build documentation
cargo doc --open --all-features

๐Ÿ“„ License

Licensed under either of:

  • Apache License, Version 2.0 (LICENSE-APACHE)

  • MIT license (LICENSE-MIT)

at your option.

๐Ÿ™ Acknowledgments

  • Thanks to all contributors who have helped shape Akita

  • Inspired by great ORMs like Diesel, SQLx, and MyBatis

  • Built with โค๏ธ by the Cat&Dog Lab team

๐Ÿ“ž Contact

  • Author: Mr.Pan

  • Email: 1049058427@qq.com

  • GitHub: @wslongchen

  • Project: Akita on GitHub

Made with โค๏ธ by Mr.Pan and the Cat&Dog Lab Team

Commit count: 216

cargo fmt