Rust Tiny Orm
一个基于sqlx的极简orm,本项目将sql代码以derive属性方式和结构体进行关联,并自动生成相关CRUD相关API,可根据需要实现任意复杂度的数据获取。
本项目开发主要原因:
- 目前rust的orm产品都太大而全了,习惯了Python Django的自动化的实现,对DSL相当不习惯,个人觉得太繁琐了;
- 实际orm就是api和sql的转换,因为orm的实现限制,sql的复杂查询的实现都比较别扭,为了与数据库交互还得额外学习大量orm dsl的用法,有点舍本求木了!
因此,是否可以将数据查询的sql和rust结合,由程序员自行控制sql的表现,这样在rust的高性能助力下,我们可以写成性能超级赞的数据库应用。
示例代码
//! 数据库模型的测试
use sqlx::mysql::MySqlPoolOptions;
use sqlx::Row;
use tiny_orm_core::prelude::*;
use super::*;
#[derive(Debug,PartialEq, Eq)]
pub struct UserType {
/// 类型编号
pub id: Option<u32>,
/// 类型名称
pub name: String,
/// 中断短信模板
pub template: String,
}
impl UserType {
/// 完整创建器
///
/// # 参数说明
///
/// * id 类型编号
/// * name 类型名称
/// * template 中断短信模板
pub fn new(id: u32, name: &str, template: &str) -> Self {
UserType {
id: Some(id),
name: name.into(),
template: template.into(),
}
}
}
/// 测试用户
/// tiny_orm 会自动实现各种数据获取和更新等方法,其中:
/// TestUser::orm_filter_with_sql等orm开头的方法,会调用TestUser::orm_row_map进行数据转换
/// TestUser::db_fetch_all_row等db开头的方法,直接返回sqlx:Row
#[allow(dead_code)]
#[derive(TinyOrm, TinyOrmQuery, Debug,PartialEq, Eq)]
#[orm_table_name_pref = "core"]
#[orm_table_name = "user"]
#[orm_select_field = "user.*,user_type.name as user_type_name, user_type.template"]
#[orm_select_join = "JOIN user_type ON user_type.id = user_type_id"]
#[orm_select_by_id_where = "user.id = ? "]
#[orm_delete_where = "id = ? "]
pub struct TestUser {
/// 类型编号
pub id: Option<u32>,
/// 类型名称
pub name: String,
/// 手机号码
pub mobile_phone: String,
/// 密码
password: String,
/// 用户类型
pub user_type: UserType
}
impl TestUser {
/// 完整创建器
///
/// # 参数说明
///
/// * id 编号
/// * name 姓名
/// * mobile_phone 手机
/// * password 密码
/// * user_type 用户类型
/// * org 对应机构
pub fn new(
id: u32,
name: &str,
mobile_phone: &str,
password: &str,
user_type: UserType,
) -> Self {
Self {
id: Some(id),
name: name.into(),
mobile_phone: mobile_phone.into(),
password: password.into(),
user_type,
}
}
}
/// 实现数据获取接口
/// tiny_orm 会自动实现各种数据获取和更新等方法,其中:
/// TestUser::orm_filter_with_sql等orm开头的方法,会调用TestUser::orm_row_map进行数据转换
/// TestUser::db_fetch_all_row等db开头的方法,直接返回sqlx:Row
impl TinyOrmData for TestUser {
/// 将sql返回数据映射为TestUser
fn orm_row_map(row: TinyOrmSqlRow) -> Self {
TestUser::new(
row.get::<u32, _>("id"),
row.get("name"),
row.get("mobile_phone"),
row.get("password"),
UserType::new(
row.get::<u32, _>("user_type_id"),
row.get("user_type_name"),
row.get("template"),
)
)
}
}
async fn get_pool() -> Result<TinyOrmDbPool> {
let user_name = "net_guard";
let password = "net_guard@20220806";
let ip = "localhost";
let port = 3306;
let db_name = "abc_net_guard";
let pool = MySqlPoolOptions::new()
.max_connections(1)
.connect(&format!(
"mysql://{}:{}@{}:{}/{}",
user_name, password, ip, port, db_name
))
.await?;
Ok(pool)
}
/// 测试SQL生成
/// 也可以使用TestUser::DB_META.build_select_sql等方法生成个性化sql
#[test]
fn test_user() {
println!("user sql : \n{}", TestUser::DB_META.select_sql);
}
/// 测试数据获取
#[test]
fn test_db_query() {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
let pool = get_pool().await.unwrap();
let data = TestUser::orm_get_all(&pool).await.unwrap();
dbg!(data);
});
}
/// 测试根据id获取用户
#[test]
fn test_orm_get_by_id() {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
let pool = get_pool().await.unwrap();
let user = TestUser::orm_get_by_id(&pool, 1).await.unwrap();
dbg!(user);
});
}
/// 测试是否存在
#[test]
fn test_orm_exists() {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
let pool = get_pool().await.unwrap();
let is_exist = TestUser::orm_exists(&pool, 1).await.unwrap();
dbg!(is_exist);
});
}
/// 测试删除
#[test]
fn test_orm_delete() {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
let pool = get_pool().await.unwrap();
TestUser::orm_delete(&pool, 6).await.unwrap();
});
}
/// 测试filter
#[test]
fn test_db_filter() {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
let pool = get_pool().await.unwrap();
let sql = TestUser::DB_META.build_select_sql("name like ? ");
println!("{sql}");
let key = String::from("%黄%");
let data = TestUser::orm_filter_with_sql(&pool, &sql, &key).await.unwrap();
dbg!(data);
});
}