easy-sqlx
介绍
根据结构体定义同步生成数据库表结构,简化增删改操作和大部分的单表查询操作,省去大部分手写 sql 语句和 bind 参数操作。
当前仅支持 postgres 数据库。
目标是帮助大部分 sql 功能开发节省心思,而不是覆盖全部的 sql 功能。
要求
1 sqlx 版本 >= 0.8
2 如果依据 struct 定义生成同步数据库表结构,请尽量不要使用 query 宏,以免造成不必要的困扰
安装
在 Cargo.toml 中添加依赖
[dependencies]
# easy-sqlx-core = { git = "https://gitee.com/knowgo/easy-sqlx.git", features = ["postgres"] }
# easy-sqlx = { git = "https://gitee.com/knowgo/easy-sqlx.git" }
tokio = { version = "1", features = ["full"] }
easy-sqlx-core = { features = ["postgres"], version = "^0" }
easy-sqlx = { version = "^0.8" }
sqlx = { version = "^0", features = [
"runtime-async-std",
"tls-native-tls",
"postgres",
"macros",
"chrono",
] }
tracing = { version = "0.1.40" }
tracing-subscriber = { version = "0.3.18" } # , features = ["env-filter"] }
chrono = { version = "0.4", features = ["serde"] }
使用说明
同步表结构
定义表结构 #[derive(Table)]
use easy_sqlx::WhereAppend; // 引用 WhereAppend
#[derive(Table, FromRow, Debug)]
#[table(
indexes [
(name = "name_index_1", columns("name"))
]
)]
#[index(columns("name"))]
struct User {
#[col(column = "key", comment = "123")]
#[col(pk)]
pub id: i64,
#[col(comment = "姓名", len = 20)]
pub name: Option<String>,
pub blob: Option<Vec<u8>>,
#[col()]
pub create_time: Option<chrono::NaiveTime>,
}
不同步表结构的话,只需要使用 Table 和 col(pk) 即可,可空字段必须为 Option<data_type>
同步表结构,参数 connection 为数据库连接
sync_tables(connection, vec![User::table()]).await?;
添加 1 全量添加
let user = User {
id: 1,
name: Some("test".to_string()),
..Default::default()
};
user.insert().execute(&mut conn).await.unwrap();
添加 2 只添加部分字段,其它字段为 null,如果没有为必填字段 set 值,大概率会出错
User::build_insert()
.set(User::id(2)) // 设置字段值,未设置的为 null
.execute(&mut conn)
.await
.unwrap();
修改 1 根据主键全量修改,如果可空字段的值为 None 则会更新对应的表中的字段值为 null
let user = User {
id: 1,
name: Some("test---1".to_string()),
..Default::default()
};
user.update().execute(&mut conn).await.unwrap();
修改 2 根据条件修改
User::build_update()
.set(User::name("007".to_string()))
.and(User::id_eq(2))
.execute(&mut conn)
.await
.unwrap();
修改 3 根据主键修改
User::update_by_id(2)
.set(User::name("by_id"))
.execute(&mut conn)
.await
.unwrap();
关于添加和修改的 set 函数
删除 1 根据主键 删除
let user = User {
id: 1, // 主键值为 1
name: Some("test---1".to_string()),
..Default::default()
};
user.delete().execute(&mut conn).await.unwrap();
删除 2 根据主键 删除
User::delete_by_id(2).execute(&mut conn).await.unwrap();
删除 3 根据条件 删除
User::build_delete()
.and(User::id_eq(2)) // 删除 id 为 2 的记录
.execute(&mut conn).await.unwrap();
对于增删改的说明
增删改调用的 execute 函数,有对应 execute_return 函数, 此函数可以返回该条语句插入、修改、或删除的记录
查询 1 根据主键查询
let u: User = User::select_by_id(1) // 联合主键会有多个参数
.one(&mut conn).await.unwrap();
println!("{:?}", u);
查询 2 根据条件查询
User::select() // 生成 SelectBuilder
.and(User::id_eq(1)) // 查询条件 id = 1
.one(&mut conn).await.unwrap();
查询 3 返回 HashMap
let umap: HashMap<i64, User> = User::select()
.map_all(&mut conn, |o: &User| o.id)
.await
.unwrap();
println!("{:?}", umap);
查询 4 查询最大值和最小值
let max: Option<i64> = User::select()
.optional_scalar(&mut conn, &User::col_id().max())
.await
.unwrap();
println!("max: {}", min.unwrap());
let min: Option<i64> = User::select()
.optional_scalar(&mut conn, &User::col_id().min())
.await
.unwrap();
println!("min: {}", min.unwrap());
分页查询
let wh = Where::new(User::id_gt(0)); // id > 0
let mut page_result: PageResult<User> = User::select()
.and(wh.clone())
.order_by(User::id_asc()) // id 升序
.page(&mut conn, &PageRequest::new(10, 1))
.await
.unwrap();
// 统计分页信息
page_result.set_total( User::select().and(wh).count(&mut conn).await.unwrap());
关于查询条件及返回
查询必要字段, UserSummary 不要同步表结构,即不需要在 sync_tables 函数的参数中出现 ,其 query_only 属性限制生成代码量
#[derive(Table, Default, Debug, Clone, FromRow)]
#[table(name = "User", query_only)] // name 属性指定表名称, query_only 表示只生成查询相关代码
pub struct UserSummary {
#[col(pk)]
pub id: i64,
pub name: String,
}
let u: UserSummary = UserSummary::select_by_id(1)
.one(&mut conn).await.unwrap();
println!("{:?}", u);
打印 sql 日志
修改 Cargo.toml
easy-sqlx-core = { features = ["postgres", "logsql"], version = "^0" }
程序启动时,订阅日志
let layer = tracing_subscriber::fmt::layer()
.with_file(true)
.with_line_number(true)
.with_filter(tracing_subscriber::filter::LevelFilter::INFO);
Registry::default().with(layer).init();