| Crates.io | akita |
| lib.rs | akita |
| version | 0.6.2 |
| created_at | 2021-07-10 13:39:53.95315+00 |
| updated_at | 2026-01-08 03:49:41.489213+00 |
| description | Akita - Mini orm for rust. |
| homepage | https://github.com/wslongchen/akita |
| repository | https://github.com/wslongchen/akita |
| max_upload_size | |
| id | 421102 |
| size | 1,993,732 |
A lightweight, fast and easy-to-use ORM framework for Rust
Add this to your Cargo.toml:
[dependencies]
akita = { version = "0.6", features = ["mysql-sync"] }
[dependencies]
akita = { version = "0.6", features = ["mysql-async"] }
[dependencies]
akita = { version = "0.6", features = ["postgres-sync"] }
[dependencies]
akita = { version = "0.6", features = ["oracle-sync"] }
[dependencies]
akita = { version = "0.6", features = ["mssql-sync"] }
[dependencies]
akita = { version = "0.6", features = ["sqlite-sync"] }
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"
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,
}
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(())
}
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(())
}
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())?;
}
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 | 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 |
tokio-postgres with blocking wrappertokio-postgres async clientoracle crate (synchronous driver)oracle crate wrapped with async runtimerusqlite crate (synchronous SQLite)sqlx with async SQLite supporttiberius with blocking APItiberius native async API| 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 |
PostgreSQL: Both sync and async use tokio-postgres, sync is just a blocking wrapper
Oracle: Sync is native driver, async is wrapper around same driver
SQLite: Different libraries for sync (rusqlite) and async (sqlx)
SQL Server: tiberius provides both sync (blocking) and async APIs
MySQL: Separate sync (mysql) and async (mysql_async) crates
TiDB/MariaDB/OceanBase: Use MySQL drivers with full compatibility
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);
}
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))?;
}
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*")
)?;
}
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)",
()
)?;
}
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'",
()
)?;
}
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
)",
()
)?;
}
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()
}
}
})?;
}
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(())
}
}
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");
}
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)
)?;
}
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])?;
}
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));
}
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);
}
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
}
MySQL/TiDB: 10-100 connections based on workload
PostgreSQL: 5-50 connections
SQLite: 1 connection (file-based)
MySQL: Multi-value INSERT statements
PostgreSQL: COPY command for large datasets
SQLite: Transactions around batch operations
Use EXPLAIN on MySQL/PostgreSQL to analyze query plans
SQLite: Use appropriate indexes and avoid expensive operations in WHERE
Reduces parsing overhead on all databases
Especially beneficial for repeated queries
Reduces connection establishment overhead
Maintains session state
MySQL/TiDB: Use connection compression for remote connections
PostgreSQL: Use prepared statements for complex queries
SQLite: Enable WAL mode for better concurrency
We welcome contributions! Here's how you can help:
Report Bugs: Create an issue with database-specific details
Suggest Features: Start a discussion about new database support or features
Submit PRs: Follow our contributing guide
Improve Documentation: Help us make the docs better for all database backends
Add Database Support: Implement support for new databases
# 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
Licensed under either of:
Apache License, Version 2.0 (LICENSE-APACHE)
MIT license (LICENSE-MIT)
at your option.
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
Author: Mr.Pan
Email: 1049058427@qq.com
GitHub: @wslongchen
Project: Akita on GitHub
Made with โค๏ธ by Mr.Pan and the Cat&Dog Lab Team