tiny_orm_macro_derive 0.2.9

基于sqlx的将SQL和ORM结合的简易ORM实现的相关辅助宏
Documentation

本库为Tiny_orm的辅助宏

Rust Tiny Orm

一个基于sqlx的极简orm,本项目将sql代码以derive属性方式和结构体进行关联,并自动生成相关CRUD相关API,可根据需要实现任意复杂度的数据获取。

本项目开发主要原因:

  1. 目前rust的orm产品都太大而全了,习惯了Python Django的自动化的实现,对DSL相当不习惯,个人觉得太繁琐了;
  2. 实际orm就是api和sql的转换,因为orm的实现限制,sql的复杂查询的实现都比较别扭,为了与数据库交互还得额外学习大量orm dsl的用法,有点舍本求木了!

因此,是否可以将数据查询的sql和rust结合,由程序员自行控制sql的表现,这样在rust的高性能助力下,我们可以写成性能超级赞的数据库应用。

特性

根据你的数据库类型选择不同特性,缺省为mysql

  1. mysql
  2. postgres
  3. sqlite
  4. mssql
  5. any

派生宏说明

为结构体自动实现数据库模型

#[derive(TinyOrm)] // 自动生成的数据库表名称前缀 // 未设置则表名称为结构名称的蛇形命名,如TestUser转换为core_test_user #[orm_table_name_pref = "core"] // 自定义数据库表名称,可选 // 未设置则表名称为结构名称的蛇形命名,如TestUser转换为test_user #[orm_table_name = "core_user"] // 设置查询返回字段SQL,可选,默认为* #[orm_select_field = "id, name"] // 设置查询表join的SQL,可选,默认为空白 #[orm_select_join = "JOIN user_type ON user_type.id = user.type_id"] // 设置按照某id字段查询where的SQL,可选,默认为id=? #[orm_select_by_id_where = "id = ? "] // 设置记录是否存在的where部分sql,可选,默认为id=? #[orm_exist_where = "id = ?"] // 设置insert sql的field和value部分,可选,默认为空白 #[orm_insert_field_and_value = "(name,mobile_phone,password,user_type_id,org_id) VALUES (?,?,?,?,?)"] // 设置删除记录的where部分sql,可选,默认为id=? #[orm_delete_where = "id = ?"] // 设置更新记录的set部分sql,可选,默认为空白 #[orm_update_set = "ip = ? , port = ?"] // 设置更新记录的where部分sql,可选,默认为空白 #[orm_update_where = "id = ?"] /// 自动生成orm_query_by查询方法,生成后的函数定义如下 /// 根据姓名和手机号查询用户 pub async fn orm_query_by_name_and_tel(pool: &TinyOrmDbPool, name:&str,mobile_phone:&str) ->Result<Vec> { let sql = Self::DB_META.build_select_sql("name = ? and mobile_phone = ?"); let query = Self::db_query(&sql) .bind(name) .bind(mobile_phone); Self::db_fetch_all(pool,query,Self::orm_row_map).await } #[orm_query( name = "name_and_tel", sql_where = "user.name = ? and mobile_phone = ?", args = "name:&str,mobile_phone:&str", doc = "根据姓名和手机号查询用户" )]

添加依赖

修改Cargo.toml,增加

[dependencies]
async-trait = "^0.1"
tiny_orm_macro_derive="^0.2"

[dependencies.tiny_orm_core]
version = "^0.2"
features = [
    "mysql",
]

示例代码

//! 数据库模型的测试
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(),
        }
    }
}
/// 测试用户

#[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 = ? "]
/// 自动生成orm_query_by查询方法,生成后的函数定义如下
/// /// 根据姓名和手机号查询用户
/// pub async fn orm_query_by_name_and_tel(pool: &TinyOrmDbPool, name:&str,mobile_phone:&str) ->Result<Vec<Self>> {
///     let sql = Self::DB_META.build_select_sql("name = ? and mobile_phone = ?");
///     let query = Self::db_query(&sql)
///         .bind(name)
///         .bind(mobile_phone);
///     Self::db_fetch_all(pool,query,Self::orm_row_map).await
/// }
#[orm_query(
    name = "name_and_tel",
    sql_where = "user.name = ? and mobile_phone = ?",
    args = "name:&str,mobile_phone:&str",
    doc = "根据姓名和手机号查询用户"
)]
#[orm_query(
    name = "name",
    sql_where = "user.name = ?",
    args = "name:&str",
    doc = "根据姓名查询用户"
)]
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,
        }
    }
}
/// 实现数据获取接口
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生成
#[test]
fn test_user() {
    println!("user sql : \n{}", TestUser::DB_META.select_sql);
}

/// .测试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_query_by_name_and_mobile() {
    tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(async {
            let pool = get_pool().await.unwrap();
            let user = TestUser::orm_query_by_name_and_tel(&pool, "张三", "1850703xxxx")
                .await
                .unwrap();
            dbg!(user);
        });
}

/// 测试根据姓名和手机获取用户
#[test]
fn test_orm_query_by_name() {
    tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(async {
            let pool = get_pool().await.unwrap();
            let user = TestUser::orm_query_by_name(&pool, "张三")
                .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);
        });
}