sqlx-sqlhelper

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

依赖

需要首先在您的Cargo.toml中添加sqlxchrono的依赖。 toml 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等方法会以此字段增删改查等。| |#[createtime]|表示当前字段为createtime字段,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| |createtime|chrono::NaiveDateTime| |updatetime|chrono::NaiveDateTime|

sql_args

sql_args声明宏主要是为了方便生成sqlxMySqlArguments对象。 rust let (sql, args) = sql_args!("user_name = ?", "张三"); 在使用base_pagebase_count等方法时,需要传递sql片段,可以通过sql_args宏生成。 rust let (sql, args) = sql_args!("user_name = ?", "张三"); let page = User::base_page(page_index, page_size, sql, args).await;

使用方法

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

该文件主要作用是配置sqlxmysql数据库的连接。 ``` rust use lazystatic::lazystatic; use sqlx::{mysql::MySqlPoolOptions, MySql, Pool}; use std::env::var;

lazystatic! { pub static ref POOL: Pool = MySqlPoolOptions::new() .maxconnections(5) .connectlazy(&format!( "mysql://{}:{}@{}/{}", var("dbuser").expect("配置文件dbuser错误"), var("dbpass").expect("配置文件dbpass错误"), var("dbhost").expect("配置文件dbhost错误"), var("dbname").expect("配置文件db_host错误"), )) .unwrap(); } ```

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

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

use sqlxsqlhelper::{commonfields, SqlHelper}; use chrono::NaiveDateTime; use poem_openapi::Object;

/// 用户表

[commonfields] //commonfields会自动添加id、createtime、updatetime字段。

[derive(sqlx::FromRow, Debug, Object, SqlHelper)]

pub struct User { /// 登录账号 pub account: String, /// 登录密码 pub pwd: String, /// 登录token pub logintoken: String, /// 登录token过期时间 pub logintokenexpiredate: NaiveDateTime, /// 最后登录时间 pub lastlogintime: NaiveDateTime, /// 最后登录ip pub lastloginip: String, } SqlHelper宏展开之后的代码。 rust // Recursive expansion of SqlHelper! macro // ========================================

impl User { pub async fn find(id: i32) -> Result { sqlx::queryas:: <_,Self>("SELECT id, account, pwd, logintoken, logintokenexpiredate, lastlogintime, lastloginip, createtime, updatetime FROM user WHERE id = ?").bind(id).fetchone(& db::POOL).await } pub async fn list() -> Result, 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 { sqlx::query("DELETE FROM user WHERE id = ?") .bind(self.id) .execute(&db::POOL) .await .map(|f| f.rowsaffected() > 0) } pub async fn insert(&self) -> Result { let sql = "INSERT INTO user (account, pwd, logintoken, logintokenexpiredate, lastlogintime, lastloginip, createtime, updatetime) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"; let lastid = sqlx::query(sql) .bind(&self.account) .bind(&self.pwd) .bind(&self.logintoken) .bind(self.logintokenexpiredate) .bind(self.lastlogintime) .bind(&self.lastloginip) .bind(self.createtime) .bind(self.updatetime) .execute(&db::POOL) .await? .last_insert_id(); Self::find(last_id as i32).await } #[doc = r" 如果定义的create_timeupdate_time字段是Default::default()默认值,则更新为当前时间"] #[doc = r""] #[doc = r" Default::default()一般为1970-01-01T00:00:00等"] pub async fn insert_auto_time(&mut self) -> Result { 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 { 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.rowsaffected() > 0) } #[doc = r" 如果定义的updatetime字段是Default::default()默认值,则更新为当前时间"] #[doc = r""] #[doc = r" Default::default()一般为1970-01-01T00:00:00等"] pub async fn updateautotime(&mut self) -> Result { if self.updatetime == Default::default() { self.updatetime = chrono::Local::now().naivelocal(); } 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 saveorupdate(&self) -> Result { match self.id > 0 { true => self.update().await, false => self.insert().await.map(|| true), } } #[doc = r" 如果定义的updatetime字段是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 saveorupdateautotime(&mut self) -> Result { match self.id > 0 { true => self.updateautotime().await, false => self.insertautotime().await.map(|| true), } } pub fn new( account: String, pwd: String, logintoken: String, logintokenexpiredate: NaiveDateTime, lastlogintime: NaiveDateTime, lastloginip: String, createtime: chrono::NaiveDateTime, updatetime: chrono::NaiveDateTime, ) -> Self { Self { id: 0, account, pwd, logintoken, logintokenexpiredate, lastlogintime, lastloginip, createtime, updatetime, } } pub fn newcommon( account: String, pwd: String, logintoken: String, logintokenexpiredate: NaiveDateTime, lastlogintime: NaiveDateTime, lastloginip: String, ) -> Self { Self::new( account, pwd, logintoken, logintokenexpiredate, lastlogintime, lastloginip, chrono::Local::now().naivelocal(), chrono::Local::now().naivelocal(), ) } pub async fn basepage( pageindex: i32, pagesize: i32, wheresql: &str, args: sqlx::mysql::MySqlArguments, ) -> Result<(Vec, i32, i32, i32), sqlx::Error> { let mut index = pageindex - 1; if index < 0 { index = 0; } let rows = pagesize; let (count,) = Self::basecount(wheresql, args.clone()).await?; let arr = match count > 0 { true => { let sql = format!("SELECT id, account, pwd, logintoken, logintokenexpiredate, lastlogintime, lastloginip, createtime, updatetime FROM user WHERE {} LIMIT {}, {}",wheresql,indexrows,rows); sqlx::query_as_with::<_, Self, sqlx::mysql::MySqlArguments>(&sql, args) .fetch_all(&db::POOL) .await? } false => Vec::new(), }; let totalpage = (count as f32 / pagesize as f32).ceil(); Ok((arr, count, index + 1, totalpage as i32)) } pub async fn basecount( wheresql: &str, args: sqlx::mysql::MySqlArguments, ) -> Result<(i32,), sqlx::Error> { let countsql = format!("SELECT count(1) FROM user WHERE {}", wheresql); sqlx::queryaswith::<_, (i32,), sqlx::mysql::MySqlArguments>(&countsql, args) .fetch_one(&*db::POOL) .await } } ```

示例

参考examples中的demo

注意