sqlx-sqlhelper
基于sqlx
和过程宏
实现的sqlhelper
生成,目前只支持mysql
数据库。
依赖
需要首先在您的Cargo.toml
中添加sqlx
和chrono
的依赖。
toml
sqlx = {version = "0.6", features = ["runtime-tokio-rustls", "mysql", "chrono", "decimal"]}
chrono = "0.4.23"
实现的宏
SqlHelper
SqlHelper
是derive
过程宏。主要实现了struct
的find
、list
、delete
、add
、update
、save_or_update
、new
、new_common
、base_page
、base_count
等常用查询方法。
|属性|描述|
|:--|:--|
|#[id]|主键字段,find
、delete
、save_or_update
等方法会以此字段增删改查等。|
|#[createtime]|表示当前字段为createtime字段,insert_auto_time
、save_or_update_auto_time
等带auto_time
后缀会自动更新create_time
字段|
|#[update_time]|和create_time
属性同理。|
common_fields
common_fields
类属性宏对常用id
、create_time
、update_time
等字段的自动添加。依赖SqlHelper
宏。
|字段名字|字段类型|
|:--|:--|
|id|i32|
|createtime|chrono::NaiveDateTime|
|updatetime|chrono::NaiveDateTime|
sql_args
sql_args
声明宏主要是为了方便生成sqlx
的MySqlArguments
对象。
rust
let (sql, args) = sql_args!("user_name = ?", "张三");
在使用base_page
、base_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
文件,代码如下。
该文件主要作用是配置sqlx
和mysql
数据库的连接。
``` 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的上下文中引入sqlx
的db
对象。
``` 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_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 {
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
注意