1 个不稳定版本

0.1.0 2023年2月12日

#2392数据库接口

Apache-2.0

15KB
61

sqlx-sqlhelper

基于sqlx过程宏实现的sqlhelper生成,目前只支持mysql数据库。

依赖

需要在您的Cargo.toml中首先添加对sqlxchrono的依赖。

sqlx = {version = "0.6", features = ["runtime-tokio-rustls", "mysql", "chrono", "decimal"]}
chrono = "0.4.23"

实现的宏

SqlHelper

SqlHelperderive过程宏。主要实现了structfindlistdeleteaddupdatesave_or_updatenewnew_commonbase_pagebase_count等常用查询方法。

属性 描述
#[id] 主键字段,finddeletesave_or_update等方法会以此字段进行增删改查等。
#[create_time] 表示当前字段为create_time字段,insert_auto_timesave_or_update_auto_time等带auto_time后缀的会自动更新create_time字段。
#[update_time] create_time属性类似。

common_fields

common_fields类属性宏用于自动添加常用idcreate_timeupdate_time等字段。依赖于SqlHelper宏。

字段名 字段类型
id i32
create_time chrono::NaiveDateTime
update_time chrono::NaiveDateTime

sql_args

sql_args声明宏主要为了方便生成sqlxMySqlArguments对象。

let (sql, args) = sql_args!("user_name = ?", "张三");

在使用base_pagebase_count等方法时,需要传递sql片段,可以通过sql_args宏生成。

let (sql, args) = sql_args!("user_name = ?", "张三");
let page = User::base_page(page_index, page_size, sql, args).await;

使用方法

1、创建一个db.rs文件,代码如下。

该文件主要作用是配置sqlxmysql数据库的连接。

use lazy_static::lazy_static;
use sqlx::{mysql::MySqlPoolOptions, MySql, Pool};
use std::env::var;

lazy_static! {
    pub static ref POOL: Pool<MySql> = MySqlPoolOptions::new()
        .max_connections(5)
        .connect_lazy(&format!(
            "mysql://{}:{}@{}/{}",
            var("db_user").expect("配置文件db_user错误"),
            var("db_pass").expect("配置文件db_pass错误"),
            var("db_host").expect("配置文件db_host错误"),
            var("db_name").expect("配置文件db_host错误"),
        ))
        .unwrap();
}

2、在struct的上下文中引入sqlxdb对象。

//此处use需要根据db.rs位置进行引用。
use super::db;

use sqlx_sqlhelper::{common_fields, SqlHelper};
use chrono::NaiveDateTime;
use poem_openapi::Object;

/// 用户表
#[common_fields] //common_fields会自动添加id、create_time、update_time字段。
#[derive(sqlx::FromRow, Debug, Object, SqlHelper)]
pub struct User {
    /// 登录账号
    pub account: String,
    /// 登录密码
    pub pwd: String,
    /// 登录token
    pub login_token: String,
    /// 登录token过期时间
    pub login_token_expire_date: NaiveDateTime,
    /// 最后登录时间
    pub last_login_time: NaiveDateTime,
    /// 最后登录ip
    pub last_login_ip: String,
}

SqlHelper宏展开之后的代码。

// Recursive expansion of SqlHelper! macro
// ========================================

impl User {
    pub async fn find(id: i32) -> Result<Self, sqlx::Error> {
        sqlx::query_as:: <_,Self>("SELECT id, account, pwd, login_token, login_token_expire_date, last_login_time, last_login_ip, create_time, update_time FROM user WHERE id = ?").bind(id).fetch_one(& *db::POOL).await
    }
    pub async fn list() -> Result<Vec<Self>, sqlx::Error> {
        sqlx::query_as:: <_,Self>("SELECT id, account, pwd, login_token, login_token_expire_date, last_login_time, last_login_ip, create_time, update_time FROM user").fetch_all(& *db::POOL).await
    }
    pub async fn delete(&self) -> Result<bool, sqlx::Error> {
        sqlx::query("DELETE FROM user WHERE id = ?")
            .bind(self.id)
            .execute(&*db::POOL)
            .await
            .map(|f| f.rows_affected() > 0)
    }
    pub async fn insert(&self) -> Result<Self, sqlx::Error> {
        let sql = "INSERT INTO user (account, pwd, login_token, login_token_expire_date, last_login_time, last_login_ip, create_time, update_time) VALUES(?, ?, ?, ?, ?, ?, ?, ?)";
        let last_id = sqlx::query(sql)
            .bind(&self.account)
            .bind(&self.pwd)
            .bind(&self.login_token)
            .bind(self.login_token_expire_date)
            .bind(self.last_login_time)
            .bind(&self.last_login_ip)
            .bind(self.create_time)
            .bind(self.update_time)
            .execute(&*db::POOL)
            .await?
            .last_insert_id();
        Self::find(last_id as i32).await
    }
    #[doc = r" 如果定义的`create_time`,`update_time`字段是`Default::default()`默认值,则更新为当前时间"]
    #[doc = r""]
    #[doc = r" `Default::default()`一般为`1970-01-01T00:00:00`等"]
    pub async fn insert_auto_time(&mut self) -> Result<Self, sqlx::Error> {
        if self.create_time == Default::default() {
            self.create_time = chrono::Local::now().naive_local();
        }
        if self.update_time == Default::default() {
            self.update_time = chrono::Local::now().naive_local();
        }
        self.insert().await
    }
    pub async fn update(&self) -> Result<bool, sqlx::Error> {
        let sql = "UPDATE user SET account = ?, pwd = ?, login_token = ?, login_token_expire_date = ?, last_login_time = ?, last_login_ip = ?, create_time = ?, update_time = ? WHERE id = ?";
        sqlx::query(sql)
            .bind(&self.account)
            .bind(&self.pwd)
            .bind(&self.login_token)
            .bind(self.login_token_expire_date)
            .bind(self.last_login_time)
            .bind(&self.last_login_ip)
            .bind(self.create_time)
            .bind(self.update_time)
            .bind(self.id)
            .execute(&*db::POOL)
            .await
            .map(|f| f.rows_affected() > 0)
    }
    #[doc = r" 如果定义的update_time字段是`Default::default()`默认值,则更新为当前时间"]
    #[doc = r""]
    #[doc = r" `Default::default()`一般为`1970-01-01T00:00:00`等"]
    pub async fn update_auto_time(&mut self) -> Result<bool, sqlx::Error> {
        if self.update_time == Default::default() {
            self.update_time = chrono::Local::now().naive_local();
        }
        self.update().await
    }
    #[doc = r" 调用`save_or_update`方法时有一定风险"]
    #[doc = r""]
    #[doc = r" `save_or_update`只是简单判断id是否大于0,大于0则更新,小于等于0则插入。"]
    #[doc = r""]
    #[doc = r" 此时如果手动将`id`赋值为大于0时,会出现更新其他数据的情况,请注意这一块。"]
    pub async fn save_or_update(&self) -> Result<bool, sqlx::Error> {
        match self.id > 0 {
            true => self.update().await,
            false => self.insert().await.map(|_| true),
        }
    }
    #[doc = r" 如果定义的update_time字段是`Default::default()`默认值,则更新为当前时间"]
    #[doc = r""]
    #[doc = r" Default::default()一般为`1970-01-01T00:00:00`等"]
    #[doc = r""]
    #[doc = r" 调用`save_or_update`方法时有一定风险"]
    #[doc = r""]
    #[doc = r" `save_or_update`只是简单判断id是否大于0,大于0则更新,小于等于0则插入。"]
    #[doc = r""]
    #[doc = r" 此时如果手动将`id`赋值为大于0时,会出现更新其他数据的情况,请注意这一块。"]
    pub async fn save_or_update_auto_time(&mut self) -> Result<bool, sqlx::Error> {
        match self.id > 0 {
            true => self.update_auto_time().await,
            false => self.insert_auto_time().await.map(|_| true),
        }
    }
    pub fn new(
        account: String,
        pwd: String,
        login_token: String,
        login_token_expire_date: NaiveDateTime,
        last_login_time: NaiveDateTime,
        last_login_ip: String,
        create_time: chrono::NaiveDateTime,
        update_time: chrono::NaiveDateTime,
    ) -> Self {
        Self {
            id: 0,
            account,
            pwd,
            login_token,
            login_token_expire_date,
            last_login_time,
            last_login_ip,
            create_time,
            update_time,
        }
    }
    pub fn new_common(
        account: String,
        pwd: String,
        login_token: String,
        login_token_expire_date: NaiveDateTime,
        last_login_time: NaiveDateTime,
        last_login_ip: String,
    ) -> Self {
        Self::new(
            account,
            pwd,
            login_token,
            login_token_expire_date,
            last_login_time,
            last_login_ip,
            chrono::Local::now().naive_local(),
            chrono::Local::now().naive_local(),
        )
    }
    pub async fn base_page(
        page_index: i32,
        page_size: i32,
        where_sql: &str,
        args: sqlx::mysql::MySqlArguments,
    ) -> Result<(Vec<Self>, i32, i32, i32), sqlx::Error> {
        let mut index = page_index - 1;
        if index < 0 {
            index = 0;
        }
        let rows = page_size;
        let (count,) = Self::base_count(where_sql, args.clone()).await?;
        let arr = match count > 0 {
            true => {
                let sql = format!("SELECT id, account, pwd, login_token, login_token_expire_date, last_login_time, last_login_ip, create_time, update_time FROM user WHERE {} LIMIT {}, {}",where_sql,index*rows,rows);
                sqlx::query_as_with::<_, Self, sqlx::mysql::MySqlArguments>(&sql, args)
                    .fetch_all(&*db::POOL)
                    .await?
            }
            false => Vec::new(),
        };
        let total_page = (count as f32 / page_size as f32).ceil();
        Ok((arr, count, index + 1, total_page as i32))
    }
    pub async fn base_count(
        where_sql: &str,
        args: sqlx::mysql::MySqlArguments,
    ) -> Result<(i32,), sqlx::Error> {
        let count_sql = format!("SELECT count(1) FROM user WHERE {}", where_sql);
        sqlx::query_as_with::<_, (i32,), sqlx::mysql::MySqlArguments>(&count_sql, args)
            .fetch_one(&*db::POOL)
            .await
    }
}

示例

参考examples中的demo

注意

依赖

~1.5MB
~37K SLoC