# Rust Tiny Orm 一个基于sqlx的极简orm,本项目将sql代码以derive属性方式和结构体进行关联,并自动生成相关CRUD相关API,可根据需要实现任意复杂度的数据获取。 本项目开发主要原因: 1. 目前rust的orm产品都太大而全了,习惯了Python Django的自动化的实现,对DSL相当不习惯,个人觉得太繁琐了; 2. 实际orm就是api和sql的转换,因为orm的实现限制,sql的复杂查询的实现都比较别扭,为了与数据库交互还得额外学习大量orm dsl的用法,为什么不直接用sql查询呢?! 3. orm为了满足各种需要,实现均较为复杂,带来了大量的学习成本。但是,实际工程角度来看,实际我们项目与数据库的交互是有限的,基本就是根据特定字段查询数据,新增数据和更新数据等,各种查询的逻辑也基本是固定的,并不需要高度灵活性的查询接口,且一般ORM实现高度灵活的查询,是以增加运行时消耗为代价的; 4. 实际orm的核心生成sql交互的代码,整个和rust的宏高度契合,因此使用宏生成我们工程需要的数据库接口,是ORM的最佳实践! 因此,是否可以将数据查询的sql和rust结合,由程序员自行控制sql的表现,这样在rust的高性能助力下,我们可以写成性能超级赞的数据库应用,并实现如下功能: 1. 自动实现rust结构体到数据库表的映射; 2. 自动生成数据库交互相关的接口,如insert、update、query、delete和exist类方法; 3. 在自动生成方法基础上,可根据sql自动生成具有高度灵活性的数据库CRUD方法; 4. 在以上基础上最大化的减少手写sql的工作量,减少数据库交互逻辑的处理工作量。 # 项目组成 本项目由两个库组成,分别为:tiny_orm_core和tiny_orm_macro_derive,其中tiny_orm_core为项目核心库,tiny_orm_macro_derive为项目派生宏库 # 特性 根据你的数据库类型选择不同特性,缺省为mysql 1. mysql 2. postgres 3. sqlite 4. mssql 5. any # 核心库说明 1. tiny_orm_core::TinyOrmDbMeta 结构体对应数据表的相关配置信息,一般由派生宏进行自动生成,并提供如下方法 * pub fn build_select_sql(&self, where_sql: &str) -> String * 创建select语句 * pub fn build_exist_sql(&self, where_sql: &str) -> String * 创建exist语句 * pub fn build_insert_sql(&self, insert_field_value: &str) -> String * 创建insert语句 * pub fn build_delete_sql(&self, delete_where_sql: &str) -> String * 创建where语句 * pub fn build_update_sql(&self, set_sql: &str, where_sql: Option<&str>) -> String * 创建update语句 * pub fn build_update_sql_with_pk(&self, set_sql: &str) -> String * 创建通过主键更新update语句 2. tiny_orm_core::TinyOrmDbModel trait,实现了db_query创建数据库查询器,方法定义如下: fn db_query(sql: &str) -> Query 3. tiny_orm_core::TinyOrmDbQuery trait,实现了各种数据库交互方法,均以db_开头,该trait中的方法不自动进行数据转换. 在派生宏无法满足需求的情况下,可使用该trait中的方法构建灵活的crud方法。 4. tiny_orm_core::TinyOrmData trait,实现了与结构体关联的相关数据库交互放阿飞,均以orm_开头,该trait中的查询类方法均实现数据库行与struct的转换,转换时调用如下方法: fn orm_row_map(row: TinyOrmSqlRow) -> Self; 该方法需要自行实现。 在派生宏无法满足需求的情况下,可使用该trait中的方法构建灵活的crud方法。 # 派生宏说明 1. #[derive(TinyOrm)] 为结构体自动实现TinyOrmDbModel数据库模型 # 用法 本宏定义了如下属性,用于设置数据映射接口和参数 # 应用于结构体的属性 1. #[orm_table_name = "core_user"] 可选属性,可设置多个。 自定义数据库表名称。 未设置则表名称为结构名称的蛇形命名, 例如:TestUser转换为test_user。 2. #[orm_table_name_pref = "core"] 可选属性,可设置多个。 为自动生成的数据库表名称增加前缀。 例如:TestUser转换为core_test_user. 3. #[orm_query] 可选属性,可设置多个。 生成Self::orm_query_with开头的获取多条记录的查询方法。 有三个子属性: * name * 生成的函数名称后缀 * 如果name设置为tel,实际生成的方法名称为:orm_query_with_tel * sql_where * 数据库select语句的where部分 * 可选,未设置则为获取所有记录 * order_by * 数据库select语句的order by部分 * 可选,未设置则为默认排序 * args * 生成方法的参数部分定义 * 参数数量应与sql_where定义的?参数数量一致 * 与实际函数定义的写法一致,如定义2个参数,格式为:arg1:type1,arg2:type2 * 实际生成的函数,会自动添加pool: &TinyOrmDbPool * doc * 生成方法的文档说明 例如: ```ignore #[orm_query( name = "name_and_tel", sql_where = "user.name = ? and mobile_phone = ?", order_by = "user.id", args = "name:&str,mobile_phone:&str", doc = "根据姓名和手机号查询用户", )] 自动生成如下函数: /// 根据姓名和手机号查询用户 pub async fn orm_query_with_name_and_tel(pool: &TinyOrmDbPool, name:&str,mobile_phone:&str) ->AnyhowResult>{...} ``` 4. #[orm_update] 可选属性,可设置多个。 生成Self::orm_update_with开头的将更新记录方法。 有三个子属性: * name * 生成的函数名称后缀 * 如果name设置为tel,实际生成的方法名称为:orm_update_with_tel * sql_set * 数据库update语句的set部分 * sql_where * 数据库update语句的where部分 * 可选,未设置则为更新所有记录 * args * 生成方法的参数部分定义 * 与实际函数定义的写法一致,如定义2个参数,格式为:arg1:type1,arg2:type2 * 参数数量应与sql_set+sql_where定义的?参数数量一致 * 实际生成的函数,会自动添加pool: &TinyOrmDbPool * doc * 生成方法的文档说明 例如: ```ignore #[orm_update( name = "name_and_tel", sql_set = "user.name = ? ,user.mobile_phone = ?", sql_where = "id = ?", args = "id:u32,name:&str,mobile_phone:&str", doc = "根据id更新姓名和手机号", )] 自动生成如下更新函数: /// 根据id更新姓名和手机号 pub async orm_update_with_name_and_tel(pool: &TinyOrmDbPool, name: &str,mobile_phone: &str) -> AnyhowResult<()>{...} ``` 5. #[orm_delete] 可选属性,可设置多个。 生成Self::orm_delete_with开头的删除记录方法。 有三个子属性: * name * 生成的函数名称后缀 * 如果name设置为tel,实际生成的方法名称为:orm_delete_with_tel * sql_where * 数据库delete语句的where部分 * 可选,未设置则为删除所有记录 * args * 生成方法的参数部分定义 * 参数数量应与sql_where定义的?参数数量一致 * 与实际函数定义的写法一致,如定义2个参数,格式为:arg1:type1,arg2:type2 * 实际生成的函数,会自动添加pool: &TinyOrmDbPool * doc * 生成方法的文档说明 例如: ```ignore #[orm_delete( name = "name_and_tel", sql_where = "user.name = ? and mobile_phone = ?" args = "name:&str,mobile_phone:&str", doc = "根据姓名和手机号删除用户" )] 自动生成如下函数: /// 根据姓名和手机号查询用户 pub async fn orm_delete_with_name_and_tel(pool: &TinyOrmDbPool, name:&str,mobile_phone:&str) ->AnyhowResult<()>{...} ``` 6. #[orm_exist] 可选属性,可设置多个。 生成Self::orm_exist_with开头的删除记录方法。 有三个子属性: * name * 生成的函数名称后缀 * 如果name设置为tel,实际生成的方法名称为:orm_exist_with_tel * sql_where * 数据库select语句的where部分 * 实际生成的sql为:select count(1) from table_name where sql_where * 可选,未设置则为所有记录 * args * 生成方法的参数部分定义 * 参数数量应与sql_where定义的?参数数量一致 * 与实际函数定义的写法一致,如定义2个参数,格式为:arg1:type1,arg2:type2 * 实际生成的函数,会自动添加pool: &TinyOrmDbPool * doc * 生成方法的文档说明 例如: ```ignore #[orm_exist( name = "id", sql_where = "id = ?" args = "id:u32", doc = "根据id查询记录是否存在" )] 自动生成如下函数: /// 根据id查询记录是否存在 pub async fn orm_exist_with_name_and_tel(pool: &TinyOrmDbPool, id: u32) ->AnyhowResult ``` 7. #[orm_join] 可选属性,可设置多个。 设置不与其他struct model关联join信息 有二个子属性: * select_field * 从join表select数据库字段的对应sql语句 * 该语句会与结构体自身对应数据库表的字段合并为select的选择字段清单 * join * 连接表的join语句 例如: ```rust #[orm_join( select_field="node.name AS node_name, organization.name AS org_name", join="JOIN node ON node.id = node_id JOIN organization ON organization.id = node.org_id", )] struct user { id:u32, name:String, /// 所属网点:连接到其他表 node_id:String, } ``` # 应用于结构体字段的方法 1. #[orm_pk(name="id", auto="true")] 至少需要设置1个,可设置多个。 设置当前字段为主键字段 有两个子属性: * name * 设置对应数据库表中的字段名称 * 可选属性,如果未设置,数据库中的字段名称和结构体字段名称一致 * auto * 设置为true,则标识这是个自动增长的主键,在插入时不设置值 * 可选属性 该属性会自动生成如下方法: 多个主键会自动汇聚为一个方法,并将主键设置为对应的函数方法的参数 * Self::orm_get_with_pk * 通过主键获取单条记录 * 如果设置了多个主键,会将按照字段定义顺序,合并到该方法的参数中 * 函数定义 * pub async fn orm_get_with_pk(pool: &TinyOrmDbPool,主键字段1:字段类型1,主键字段2:字段类型2,...) -> AnyhowResult * 结构体字段类型自动转换 * Option 自动转换为 T * String 自动转换为 &str * Self::orm_delete_with_pk * 通过主键删除记录 * 如果设置了多个主键,会将按照字段定义顺序,合并到该方法的参数中 * 函数定义 * pub async fn orm_delete_with_pk(pool: &TinyOrmDbPool,主键字段1:字段类型1,主键字段2:字段类型2,...) -> AnyhowResult<()> * 结构体字段类型自动转换 * Option 自动转换为 T * String 自动转换为 &str * self::orm_delete * 通过主键删除当前实例在数据库记录 * 如果设置了多个主键,会将按照字段定义顺序,合并到该方法的参数中 * 函数定义 * pub async fn orm_delete(&self,pool: &TinyOrmDbPool) -> AnyhowResult * Self::orm_exist_with_pk * 通过主键查询记录是否存在 * 如果设置了多个主键,会将按照字段定义顺序,合并到该方法的参数中 * 函数定义 * pub async fn orm_exists_with_pk(pool: &TinyOrmDbPool,主键字段1:字段类型1,主键字段2:字段类型2,...) -> AnyhowResult * 结构体字段类型自动转换 * Option 自动转换为 T * String 自动转换为 &str * Self::orm_exist * 通过主键查询当前实例在数据库记录是否存在 * 如果设置了多个主键,会将按照字段定义顺序,合并到该方法的参数中 * 函数定义 * pub async fn orm_exists_with_pk(&self,pool: &TinyOrmDbPool) -> AnyhowResult # 示例 ```ignore struct TestUser { #[orm_pk(name="user_id",auto="true")] id:u32, #[orm_pk] tel:String, } //以上会自动生成如下方法 pub async fn orm_get_with_pk(pool: &TinyOrmDbPool,id:u32,tel:&str) -> AnyhowResult{...} pub async fn orm_delete_with_pk(pool: &TinyOrmDbPool,id:u32,tel:&str) -> AnyhowResult<()>{...} pub async fn orm_exist_with_pk(pool: &TinyOrmDbPool,id:u32,tel:&str) -> AnyhowResult{...} pub async fn orm_delete(self,pool: &TinyOrmDbPool) -> AnyhowResult{...} pub async fn orm_exist(self,pool: &TinyOrmDbPool) -> AnyhowResult{...} ``` 2. #[orm_join] 可选属性,可设置多个。 设置对应字段为关联其他表的join字段 有六个子属性: * name * 设置对应数据库表中的字段名称 * 可选属性,如果未设置,数据库中的字段名称和结构体字段名称一致 * select_field * 从join表select数据库字段的对应sql语句 * 该语句会与结构体自身对应数据库表的字段合并为select的选择字段清单 * join * 连接表的join语句 * link_id * 字段的rust类型的对应连接字段 * 例如,如某结构体User * 其中某一字段定义为:user_type:UserType,设置link_id="id" * 写入User表记录时,会自动读取self.user_type.id为对应数据库字段的值 * link_id_type * 连接字段的rust类型 * 如以上UserType::id为u32类型,则link_id_type="u32" * skip_method * 是否不自动生成相关join方法 * true,则不自动生成 如为设置skip_method="true",该属性会自动生成如下方法: * Self::orm_query_join_with_字段名称 * 使用join字段查询记录 * 函数定义 * pub async fn orm_query_join_with_字段名称(pool: &TinyOrmDbPool,字段名称:字段类型) -> AnyhowResult> * 结构体字段类型自动转换 * Option 自动转换为 T * String 自动转换为 &str * Self::orm_query_join_with_字段名称_{link_id名称} * 使用join字段的link_id查询记录 * 函数定义 * pub async fn orm_query_join_with_字段名称_{link_id名称}(pool: &TinyOrmDbPool,link_id名称:link_id_type名称) -> AnyhowResult> * Self::orm_delete_join_with_字段名称 * 使用join字段删除记录 * 函数定义 * pub async fn orm_delete_join_with_字段名称(pool: &TinyOrmDbPool,字段名称:字段类型) -> AnyhowResult<()> * 结构体字段类型自动转换 * Option 自动转换为 T * String 自动转换为 &str * Self::orm_delete_join_with_字段名称_{link_id名称} * 使用join字段的link_id删除记录 * 函数定义 * pub async fn orm_delete_join_with_字段名称_{link_id名称}(pool: &TinyOrmDbPool,link_id名称:link_id_type名称) -> AnyhowResult<()> * self::orm_update_join_with_字段名称 * 使用当前实例的对应join字段.link_id保存到数据库中对应字段中 * 函数定义 * pub async fn orm_update_join_with_字段名称(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()> # 示例 ``` struct User { #[orm_join( name="user_type_id", select_field="user_type.name as user_type_name, user_type.template", join="JOIN user_type ON user_type.id = user_type_id", link_id="id", link_id_type="u32", )] user_type:UserType } // 会自动生成如下方法 pub async fn orm_query_join_with_user_type(pool: &TinyOrmDbPool,user_type:UserType) -> AnyhowResult>{...} pub async fn orm_query_join_with_user_type_id(pool: &TinyOrmDbPool,query_value:u32) -> AnyhowResult>{...} pub async fn orm_delete_join_with_user_type(pool: &TinyOrmDbPool,user_type:UserType) -> AnyhowResult<()>{...} pub async fn orm_delete_join_with_user_type_id(pool: &TinyOrmDbPool,query_value:u32) -> AnyhowResult<()>{...} pub async fn orm_update_join_with_user_type(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()>{...} ``` 3. #[orm_field(name = "custom_table_field_name")] 可选属性,可设置多个。 设置对应数据库表的字段名称 有1个子属性: * name * 设置对应数据库表中的字段名称 * 可选属性,如果未设置,数据库中的字段名称和结构体字段名称一致 4. #[orm_ignore] 可选属性,可设置多个。 默认情况下,结构体的每个字段均对应一个数据库表字段。 设置该属性后,可忽略对应字段,不对应数据库表的字段名称 5. #[orm_update] 生成更新数据库中的对应字段值的方法,方法定义如下: pub async fn orm_update_字段名称(&self,pool: &TinyOrmDbPool) ->AnyhowResult<()> 6. #[orm_query] * order_by * 数据库select语句的order by部分 * 可选,未设置则为默认排序 生成使用该字段查询数据库记录的方法,方法定义如下: * pub async fn orm_query_with_字段名称(pool: &TinyOrmDbPool,query_value:字段类型) -> AnyhowResult> * 结构体字段类型自动转换 * Option 自动转换为 T * String 自动转换为 &str # 示例 ```rust struct User { id:String, #[orm_query( order_by="user.id", )] name:String, #[orm_query] sex:String, user_type:UserType, } ``` 7. #[orm_get] 生成使用该字段获取单条数据库记录的方法,方法定义如下: * pub async fn orm_get_with_字段名称(pool: &TinyOrmDbPool,query_value:字段类型) -> AnyhowResult * 结构体字段类型自动转换 * Option 自动转换为 T * String 自动转换为 &str # 其他自动生成方法 1. self.orm_insert方法 将当前结构体实例数据插入到数据库表中,用于新增记录的保存 方法定义如下: pub async fn orm_insert(&mut self, pool: &TinyOrmDbPool) -> AnyhowResult<()> 2. self.orm_update_all方法 将当前结构体实例数据更新到数据库表中,用于变更记录的保存 方法定义如下: pub async fn orm_update_all(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()> 2. #[derive(TinyOrmQuery)] 自动实现TinyOrmQuery trait宏 # 使用说明 1. 添加依赖 修改Cargo.toml,增加 ``` [dependencies] async-trait = "^0.1" tiny_orm_macro_derive="^0.3" [dependencies.tiny_orm_core] version = "^0.3" features = [ "mysql", ] ``` 2. 引入相关trait ```ignore use tiny_orm_core::prelude::*; ``` 1. 在结构体中使用派生属性 ``` struct User{ #[orm_pk(auto="true")] id: u32, name: String, } ``` 1. 使用自动生成的相关orm_方法进行数据库交互 # 示例代码 ``` //! 数据库模型的测试 use sqlx::mysql::MySqlPoolOptions; use sqlx::Row; use tiny_orm_macro_derive::{TinyOrm, TinyOrmQuery}; use super::*; #[derive(Debug, PartialEq, Eq)] pub struct UserType { /// 类型编号 pub id: Option, /// 类型名称 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(), } } } #[derive(Debug, PartialEq, Eq)] pub struct Organization { /// 行号 pub id: String, /// 机构名称 pub name: String, } #[allow(dead_code)] #[derive(TinyOrm, TinyOrmQuery, Debug, PartialEq, Eq)] // 自动生成表格名称时附加前缀,生成表名称为:core_test_user //#[orm_table_name_pref = "core"] // 指定表名称,未指定则自动生成,规则结构名称转换为蛇形命名,如:test_user #[orm_table_name = "user"] /// 自动生成orm_query_with查询方法,生成后的函数定义如下 /// /// 根据姓名和手机号查询用户 /// pub async fn orm_query_with_name_and_tel(pool: &TinyOrmDbPool, name:&str,mobile_phone:&str) ->AnyhowResult> { /// 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_delete_with删除方法,生成后的函数定义如下 /// /// 根据姓名和手机号删除用户 /// pub async fn orm_delete_with_name_and_tel(pool: &TinyOrmDbPool, name:&str,mobile_phone:&str) ->AnyhowResult> { /// let sql = Self::DB_META.build_delete_sql("name = ? and mobile_phone = ?"); /// let query = Self::db_query(&sql) /// .bind(name) /// .bind(mobile_phone); /// Self::db_execute(pool, query).await /// } #[orm_delete( name = "name_and_tel", sql_where = "user.name = ? and mobile_phone = ?", args = "name:&str,mobile_phone:&str", doc = "根据姓名和手机号删除用户" )] /// 生成orm_exist_with_name方法 #[orm_exist( name = "name", sql_where = "user.name like ?", args = "name:&str", doc = "根据姓名查询用户是否存在" )] /// 生成orm_update_with_name方法 #[orm_update( name = "name_and_tel", sql_set = "name = ? , mobile_phone = ?", sql_where = "id = ?", args = "name:&str,mobile_phone:&str,id:u32", doc = "根据id更新姓名和手机号" )] pub struct TestUser { /// 类型编号 /// 生成的orm_get_by_pk函数参数中,id转换为u32 /// 会自动将多个pk字段合并为一个方法的参数,生成如下方法 /// /// orm_pk自动实现:通过主键获取记录 /// pub async fn orm_get_by_pk(pool: &TinyOrmDbPool, id:u32,mobile_phone:&str) -> AnyhowResult{ /// let sql = Self::DB_META.build_select_sql(#pk_where_sql); /// let query = Self::db_query(&sql) /// .bind(id) /// .bind(mobile_phone); /// Self::db_fetch_one(pool, query, Self::orm_row_map).await /// .with_context(|| "根据主键获取记录出错!") /// } /// /// orm_pk自动实现:通过主键删除记录 /// pub async fn orm_delete_by_pk(pool: &TinyOrmDbPool,id:u32,mobile_phone:&str) -> AnyhowResult<()>{ /// let sql = Self::DB_META.build_delete_sql(#pk_where_sql); /// let query = Self::db_query(&sql) /// .bind(id) /// .bind(mobile_phone); /// Self::db_execute(pool, query).await /// .with_context(|| "根据主键删除记录出错!")?; /// Ok(()) /// } /// /// orm_pk自动实现:通过主键删除当前记录 /// pub async fn orm_delete(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()>{ /// Self::orm_delete_by_pk(pool,self.id.unwrap(),self.mobile_phone.as_ref()).await /// } /// /// orm_pk自动实现:通过主键查询记录是否存在 /// pub async fn orm_exist_with_pk(pool: &TinyOrmDbPool,,id:u32,mobile_phone:&str) -> AnyhowResult{ /// let sql = Self::DB_META.build_exist_sql(Self::DB_META.pk_where_sql); /// let row: (i64,) = sqlx::query_as(&sql) /// .bind(id) /// .bind(mobile_phone) /// .fetch_one(pool) /// .await /// .with_context(|| "通过主键查询记录是否存在时出错!")?; /// Ok(row.0 > 0) /// } /// /// orm_pk自动实现:通过主键查询当前记录是否存在 /// pub async fn orm_exist(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()>{ /// Self::orm_exist_with_pk(pool,self.id.unwrap(),self.mobile_phone.as_ref()).await /// } /// 只能设置一个auto主键 #[orm_pk(name = "id", auto = "true")] /// insert时自动生成id pub id: Option, /// 类型名称 // 生成orm_query_with_name方法 #[orm_query] pub name: String, /// 手机号码 /// 生成的orm_get_by_pk函数参数中,mobile_phone会自动把String转换为&str #[orm_pk] pub mobile_phone: String, /// 密码 /// 重命名数表字段名称,否则与字段名称一致 #[orm_field(name = "password")] // 生成self.orm_update_password方法 #[orm_update] password: String, /// 用户类型 /// 定义join字段和join sql /// 自动生成Self::orm_query_join_with_user_type,Self::orm_delete_join_with_user_type,self.orm_update_join_with_user_type方法 #[orm_join( name="user_type_id", // 重命名表字段名称 select_field="user_type.name as user_type_name, user_type.template", join="JOIN user_type ON user_type.id = user_type_id", link_id="id",//update 时保存值使用的字段id,如:user_type.id link_id_type="u32" )] #[allow(unused_variables)] pub user_type: UserType, /// 所属机构 /// 自动生成Self::orm_query_join_with_org,Self::orm_delete_join_with_org,self.orm_update_join_with_org方法 #[orm_join( name="org_id", // 重命名表字段名称 select_field="organization.name as org_name", join="JOIN organization ON organization.id = org_id", link_id="id",//update 时保存值使用的字段id,如:org.id link_id_type="&str" )] pub org: Organization, /// 忽略字段,不在数据库中对应 #[orm_ignore] pub ignore_field: u32, } 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, org: Organization, ) -> Self { Self { id: Some(id), name: name.into(), mobile_phone: mobile_phone.into(), password: password.into(), user_type, org, ignore_field: 0, } } /// 完整创建器 /// /// # 参数说明 /// /// * id 编号 /// * name 姓名 /// * mobile_phone 手机 /// * password 密码 /// * user_type 用户类型 /// * org 对应机构 pub fn new_no_id( name: &str, mobile_phone: &str, password: &str, user_type: UserType, org: Organization, ) -> Self { Self { id: None, name: name.into(), mobile_phone: mobile_phone.into(), password: password.into(), user_type, org, ignore_field: 0, } } } /// 实现数据获取接口 impl TinyOrmData for TestUser { /// 将sql返回数据映射为TestUser fn orm_row_map(row: TinyOrmSqlRow) -> Self { TestUser::new( row.get::("id"), row.get("name"), row.get("mobile_phone"), row.get("password"), UserType::new( row.get::("user_type_id"), row.get("user_type_name"), row.get("template"), ), Organization { id: row.get("org_id"), name: row.get("org_name"), }, ) } } async fn get_pool() -> AnyhowResult { let user_name = "net_guard"; let password = "some pass"; 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); }); } /// 测试根据姓名和手机获取用户 #[test] fn test_orm_query_with_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_with_name_and_tel(&pool, "张三", "1850703xxxx") .await .unwrap(); dbg!(user); }); } /// 测试根据主键获取获取用户 #[test] fn test_orm_get_with_pk() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); let user = TestUser::orm_get_with_pk(&pool, 2, "1387038xxxx") .await .unwrap(); dbg!(user); }); } /// 测试根据主键删除用户 #[test] fn test_orm_delete() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); let user = TestUser::orm_get_with_pk(&pool, 15, "1867930xxxx") .await .unwrap(); user.orm_delete(&pool).await.unwrap(); dbg!(user); }); } /// 测试根据个性删除,根据名称和手机号删除用户 #[test] fn test_orm_delete_with_name_and_tel() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); TestUser::orm_delete_with_name_and_tel(&pool, "盛XX", "1507031xxxx") .await .unwrap(); }); } /// 测试根据姓名和手机获取用户 #[test] fn test_orm_query_with_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_with_name(&pool, "张三").await.unwrap(); dbg!(user); }); } /// 测试是否存在 #[test] fn test_orm_exist_with_pk() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); let is_exist = TestUser::orm_exist_with_pk(&pool, 8, "18607031111") .await .unwrap(); dbg!(is_exist); }); } /// 测试是否存在 #[test] fn test_orm_exist_with_name() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); let is_exist = TestUser::orm_exist_with_name(&pool, "王xx") .await .unwrap(); dbg!(is_exist); }); } /// 测试根据姓名和手机获取用户 #[test] fn test_orm_update_with_name_and_tel() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); let user = TestUser::orm_update_with_name_and_tel(&pool, "张三", "18507032200", 4) .await .unwrap(); dbg!(user); }); } /// 测试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("user.name like ? "); println!("{sql}"); let key = String::from("%李%"); let data = TestUser::orm_filter_with_sql(&pool, &sql, &key) .await .unwrap(); dbg!(data); }); } /// 测试join字段查询和更新 #[test] fn test_orm_query_join_with_user_type() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); let user_type = UserType::new(1, "支行", ""); let mut data = TestUser::orm_query_join_with_user_type(&pool, user_type) .await .unwrap(); let mut ygl = data.pop().unwrap(); let user_type_2 = UserType::new(2, "", ""); ygl.user_type = user_type_2; ygl.orm_update_join_with_user_type(&pool).await.unwrap(); dbg!(ygl); }); } /// 测试join字段查询和更新 #[test] fn test_orm_query_join_with_org_id() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); let data = TestUser::orm_query_join_with_org_id(&pool, "14H700") .await .unwrap(); dbg!(data); }); } /// 测试join字段查询和更新 #[test] fn test_orm_delete_join_with_user_type() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); let user_type = UserType::new(2, "支行", ""); TestUser::orm_delete_join_with_user_type(&pool, user_type) .await .unwrap(); }); } /// 测试更新所有信息 #[test] fn test_orm_update_all() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); let mut user = TestUser::orm_get_with_pk(&pool, 2, "13807931111") .await .unwrap(); let user_type = UserType::new(2, "支行", ""); user.name = "王2".into(); user.user_type = user_type; user.orm_update_all(&pool).await.unwrap(); dbg!(user); }); } /// 测试插入信息 #[test] fn test_orm_insert() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); let org = Organization { id: "14H700".into(), name: "上饶分行".into(), }; let user_type = UserType::new(2, "管理员", ""); let mut user = TestUser::new_no_id("李四", "1850703xxxx", "sss", user_type, org); user.orm_insert(&pool).await.unwrap(); dbg!(user); }); } /// 测试根据姓名和手机获取用户 #[test] fn test_orm_update_password() { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() .block_on(async { let pool = get_pool().await.unwrap(); let mut user:TestUser = TestUser::orm_query_with_name(&pool, "张三").await.unwrap().pop().unwrap(); user.password="this is a pass".into(); user.orm_update_password(&pool).await.unwrap(); dbg!(user); }); } ```