18个版本
0.4.5 | 2023年8月18日 |
---|---|
0.4.4 |
|
0.3.1 | 2022年11月18日 |
0.3.0 | 2022年10月6日 |
0.2.10 | 2022年9月25日 |
#2740 in 数据库接口
每月 103次下载
用于 tiny_orm_core
130KB
1.5K SLoC
Rust Tiny Orm
一个基于sqlx的极简ORM,本项目通过derive属性方式和结构体关联SQL代码,并自动生成相关CRUD API,可根据需要实现任意复杂度的数据获取。
本项目开发主要原因:
- 目前Rust的ORM产品都过于庞大而全面,习惯了Python Django的自动化实现,对DSL相当不习惯,个人觉得太繁琐了;
- 实际ORM就是API和SQL的转换,因为ORM的实现限制,复杂的SQL查询实现都比较别扭,为了与数据库交互还得额外学习大量ORM DSL的用法,为什么不直接用SQL查询呢?!
- ORM为了满足各种需求,实现均较为复杂,带来了大量的学习成本。但是,从实际工程角度来看,实际项目与数据库的交互是有限的,基本就是根据特定字段查询数据,新增数据和更新数据等,各种查询的逻辑也基本是固定的,并不需要高度灵活性的查询接口,而且一般ORM实现高度灵活的查询是以增加运行时消耗为代价的;
- 实际ORM的核心生成SQL交互的代码,整个和Rust的宏高度契合,因此使用宏生成我们工程需要的数据库接口,是ORM的最佳实践!
因此,是否可以将数据查询的SQL和Rust结合,由程序员自行控制SQL的表现,这样在Rust的高性能助力下,我们可以写成性能超级赞的数据库应用,并实现如下功能:
- 自动实现Rust结构体到数据库表的映射;
- 自动生成数据库交互相关的接口,如insert、update、query、delete和exist类方法;
- 在自动生成方法的基础上,可根据SQL自动生成具有高度灵活性的数据库CRUD方法;
- 在以上基础上最大化的减少手写SQL的工作量,减少数据库交互逻辑的处理工作量。
项目组成
本项目由两个库组成,分别为:tiny_orm_core和tiny_orm_macro_derive,其中tiny_orm_core为项目核心库,tiny_orm_macro_derive为项目派生宏库
特性
根据你的数据库类型选择不同特性,默认为mysql
- mysql
- postgres
- sqlite
- mssql
- any
核心库说明
- 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语句
- tiny_orm_core::TinyOrmDbModel
trait,实现了db_query创建数据库查询器,方法定义如下:
fn db_query(sql: &str) -> Query<MySql, MySqlArguments>
- tiny_orm_core::TinyOrmDbQuery
trait,实现了各种数据库交互方法,均以db_开头,该trait中的方法不自动进行数据转换.
在派生宏无法满足需求的情况下,可使用该trait中的方法构建灵活的crud方法。
- tiny_orm_core::TinyOrmData
trait,实现了与结构体关联的相关数据库交互方法,均以orm_开头,该trait中的查询类方法均实现数据库行与struct的转换,转换时调用如下方法: fn orm_row_map(row: TinyOrmSqlRow) -> Self; 该方法需要自行实现。
在派生宏无法满足需求的情况下,可使用该trait中的方法构建灵活的crud方法。
派生宏说明
- #[derive(TinyOrm)]
为结构体自动实现TinyOrmDbModel数据库模型
用法
本宏定义了如下属性,用于设置数据映射接口和参数
应用于结构体的属性
- #[orm_table_name = "core_user"]
可选属性,可设置多个。
自定义数据库表名称。 未设置则表名称为结构名称的蛇形命名, 例如:TestUser转换为test_user。
- #[orm_table_name_pref = "core"]
可选属性,可设置多个。
为自动生成的数据库表名称增加前缀。 例如:TestUser转换为core_test_user.
- #[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
- 生成方法的文档说明
例如:
#[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<Vec<Self>>{...}
- #[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
- 生成方法的文档说明
例如:
#[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<()>{...}
- #[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
- 生成方法的文档说明
例如:
#[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<()>{...}
- #[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
- 可选,未设置则为所有记录
- 数据库select语句的where部分
- args
- 生成方法的参数部分定义
- 参数数量应与sql_where定义的?参数数量一致
- 与实际函数定义的写法一致,如定义2个参数,格式为:arg1:type1,arg2:type2
- 实际生成的函数,会自动添加pool: &TinyOrmDbPool
- doc
- 生成方法的文档说明
例如:
#[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<bool>
- #[orm_join]
可选属性,可设置多个。
设置不与其他struct model关联join信息
有二个子属性:
- select_field
- 从join表select数据库字段的对应sql语句
- 该语句会与结构体自身对应数据库表的字段合并为select的选择字段清单
- join
- 连接表的join语句
例如:
#[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,
}
应用于结构体字段的方法
- #[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
示例
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<Self>{...}
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<bool>{...}
pub async fn orm_delete(self,pool: &TinyOrmDbPool) -> AnyhowResult<bool>{...}
pub async fn orm_exist(self,pool: &TinyOrmDbPool) -> AnyhowResult<bool>{...}
- #[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<Vec>
- 结构体字段类型自动转换
- 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<Vec>
- 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<Vec<Self>>{...}
pub async fn orm_query_join_with_user_type_id(pool: &TinyOrmDbPool,query_value:u32) -> AnyhowResult<Vec<Self>>{...}
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<()>{...}
- #[orm_field(name = "custom_table_field_name")]
可选属性,可设置多个。
设置对应数据库表的字段名称
有1个子属性:
- name
- 设置对应数据库表中的字段名称
- 可选属性,如果未设置,数据库中的字段名称和结构体字段名称一致
- #[orm_ignore]
可选属性,可设置多个。
默认情况下,结构体的每个字段均对应一个数据库表字段。 设置该属性后,可忽略对应字段,不对应数据库表的字段名称
- #[orm_update]
生成更新数据库中的对应字段值的方法,方法定义如下: pub async fn orm_update_字段名称(&self,pool: &TinyOrmDbPool) ->AnyhowResult<()>
- #[orm_query]
- order_by
- 数据库select语句的order by部分
- 可选,未设置则为默认排序
生成使用该字段查询数据库记录的方法,方法定义如下
- pub async fn orm_query_with_字段名称(pool: &TinyOrmDbPool,query_value:字段类型) -> AnyhowResult<Vec>
- 结构体字段类型自动转换
- Option 自动转换为 T
- String 自动转换为 &str
示例
struct User {
id:String,
#[orm_query(
order_by="user.id",
)]
name:String,
#[orm_query]
sex:String,
user_type:UserType,
}
- #[orm_get]
生成使用该字段获取单条数据库记录的方法,方法定义如下
- pub async fn orm_get_with_字段名称(pool: &TinyOrmDbPool,query_value:字段类型) -> AnyhowResult
- 结构体字段类型自动转换
- Option 自动转换为 T
- String 自动转换为 &str
其他自动生成方法
- self.orm_insert方法
将当前结构体实例数据插入到数据库表中,用于新增记录的保存
方法定义如下: pub async fn orm_insert(&mut self, pool: &TinyOrmDbPool) -> AnyhowResult<()>
- self.orm_update_all方法
将当前结构体实例数据更新到数据库表中,用于变更记录的保存
方法定义如下: pub async fn orm_update_all(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()>
- #[derive(TinyOrmQuery)]
自动实现TinyOrmQuery trait宏
使用说明
- 添加依赖
修改Cargo.toml,增加
[dependencies]
async-trait = "^0.1"
tiny_orm_macro_derive="^0.3"
[dependencies.tiny_orm_core]
version = "^0.3"
features = [
"mysql",
]
- 引入相关trait
use tiny_orm_core::prelude::*;
- 在结构体中使用派生属性
struct User{
#[orm_pk(auto="true")]
id: u32,
name: String,
}
- 使用自动生成的相关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<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(),
}
}
}
#[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<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_delete_with删除方法,生成后的函数定义如下
/// /// 根据姓名和手机号删除用户
/// pub async fn orm_delete_with_name_and_tel(pool: &TinyOrmDbPool, name:&str,mobile_phone:&str) ->AnyhowResult<Vec<Self>> {
/// 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<Self>{
/// 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<bool>{
/// 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<u32>,
/// 类型名称
// 生成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::<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"),
),
Organization {
id: row.get("org_id"),
name: row.get("org_name"),
},
)
}
}
async fn get_pool() -> AnyhowResult<TinyOrmDbPool> {
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);
});
}
Dependencies
~1.5MB
~34K SLoC