pgbatis
pgbaits 编写规则纯属个人习惯与风格,不喜勿用。
轻量级异步 PostgreSQL CRUD 辅助库,基于 tokio-postgres + deadpool-postgres。
通过派生宏 #[derive(PGCRUD)] 快速为结构体生成增删改查能力,提供链式条件构造器(Wrapper)和可选逻辑删除支持。
特性
- 派生宏驱动 —
#[derive(PGCRUD)] 自动生成 INSERT/UPDATE/SELECT SQL 及 Parameters trait 实现
- 链式条件构造 —
Wrapper 提供 eq、like、between、gt、set_pages 等链式方法,统一使用 $n 参数化查询
- 逻辑删除 — 默认启用,读操作自动过滤已删除数据,
remove() 默认软删除,支持按表排除
- 分页查询 — 单表
fetch_page 和原生 SQL query_page 均自动计算 total、pages
- 事务支持 —
transaction_delete_and_save、transaction_batch 支持跨表批量操作
- 灵活查询 — 类型化查询(
fetch/fetch_one)与 HashMap 查询(query/query_one)双模式
快速开始
1. 添加依赖
[dependencies]
pgbatis = "0.1"
serde = { version = "1.0", features = ["derive"] }
2. 定义实体
use pgbatis::pgmacro::PGCRUD;
use serde::{Deserialize, Serialize};
#[derive(PGCRUD, Serialize, Deserialize, Clone, Debug, Default)]
pub struct User {
pub id: Option<String>,
pub name: Option<String>,
pub age: Option<i32>,
}
#[derive(PGCRUD)] 还会自动生成对应的 UserColumn 结构体(如 UserColumn::Id、UserColumn::Name),
用于在 Wrapper 中引用列名,避免硬编码字符串。
3. 初始化连接
pgbatis::link("127.0.0.1", 5432, "user", "password", "mydb", 10).await?;
4. CRUD 操作
use pgbatis::Wrapper;
let user = User { id: Some("1".into()), name: Some("Alice".into()), age: Some(30) };
pgbatis::save(&user, None, None).await?;
let update = User { name: Some("Bob".into()), ..Default::default() };
let wrapper = Wrapper::new().eq(&UserColumn::Id, &"1");
pgbatis::update(update, wrapper, None, None).await?;
let users: Vec<User> = pgbatis::fetch(Wrapper::new().gt(&UserColumn::Age, &18), None, None).await?;
let user: User = pgbatis::fetch_one(Wrapper::new().eq(&UserColumn::Id, &"1"), None, None).await?;
let page = pgbatis::fetch_page::<User>(
Wrapper::new().set_pages(1, 10).set_order_by_column_ext(&UserColumn::Id, false),
None, None,
).await?;
println!("第{}/{}页,共{}条", page.current_page, page.pages, page.total);
pgbatis::remove::<User>(Wrapper::new().eq(&UserColumn::Id, &"1"), None, None).await?;
pgbatis::permanent_deletion::<User>(Wrapper::new().eq(&UserColumn::Id, &"1"), None, None).await?;
API 总览
连接管理
| 函数 |
说明 |
link(host, port, user, password, dbname, max_size) |
初始化连接池(必须最先调用) |
ping() |
连接探活(执行 SELECT 1) |
CRUD 操作
| 函数 |
说明 |
save(entity, prefix, suffix) |
INSERT,SQL 由 Parameters::gen_save 自动生成 |
update(entity, wrapper, prefix, suffix) |
UPDATE,结合 Wrapper 条件 |
remove::<T>(wrapper, prefix, suffix) |
删除(默认软删除,排除表则物理删除) |
permanent_deletion::<T>(wrapper, prefix, suffix) |
强制物理 DELETE |
类型化查询(返回实体)
| 函数 |
说明 |
fetch::<T>(wrapper, prefix, suffix) |
单表查询,返回 Vec<T> |
fetch_one::<T>(wrapper, prefix, suffix) |
单表查询单条,返回 T |
fetch_page::<T>(wrapper, prefix, suffix) |
单表分页,返回 PageT<T> |
fetch_with_recoder_field::<T>(…) |
单表查询,返回 Vec<HashMap<String, Value>> |
原生 SQL 查询(返回 HashMap)
| 函数 |
说明 |
query(sql, params) |
查询列表 → Vec<HashMap<String, Value>> |
query_one(sql, params) |
查询单条 → HashMap<String, Value> |
query_page(sql, params, order_by, desc, page, size) |
分页查询 → PageHash |
query_t::<T>(sql, params) |
查询列表 → Vec<T>(SELECT 字段顺序须与实体一致) |
execute(sql, params) |
执行非查询 SQL → 影响行数 |
存在性检查
| 函数 |
说明 |
check_row_by_column(table, column, value) |
按列值检查是否存在 |
check_row_wrap::<T>(wrapper, prefix, suffix) |
按 Wrapper 条件检查是否存在 |
事务
| 函数 |
说明 |
transaction_delete_and_save(…) |
先删后批量插(事务内) |
transaction_batch(params) |
批量执行多条 SQL(可跨表) |
工具函数
| 函数 |
说明 |
format_like(s) |
"hello" → Some("%hello%") |
format_like_left(s) |
"hello" → Some("%hello") |
format_like_right(s) |
"hello" → Some("hello%") |
set_unlogic_delete_table(table) |
将表加入逻辑删除排除名单 |
Wrapper 条件构造器
use pgbatis::Wrapper;
let wrapper = Wrapper::new()
.eq(&UserColumn::Name, &"Alice")
.not_eq(&OrderColumn::Status, &0)
.gt(&UserColumn::Age, &18)
.ge(&OrderColumn::Score, &60)
.lt(&OrderColumn::Price, &100)
.le(&OrderColumn::Stock, &50)
.between(&OrderColumn::CreatedAt, &start, &end)
.not_between(&OrderColumn::Price, &min, &max)
.like(&UserColumn::Name, &format!("%{}%", keyword))
.set_in(&UserColumn::Id, &some_value)
.in_array_string(&OrderColumn::Status, &vec!["active".into(), "pending".into()])
.is_null::<String>(&OrderColumn::DeletedAt)
.is_not_null::<String>(&OrderColumn::Email)
.set_recoder_field(&UserColumn::Id)
.set_recoder_field(&UserColumn::Name)
.set_order_by_column_ext(&UserColumn::Id, false) .set_pages(1, 20);
let (where_clause, args) = wrapper.build(1).unwrap();
逻辑删除
默认行为
- 读操作(
fetch/fetch_one/fetch_page/check_row_*)自动追加 is_deleted = 0 过滤
remove() 执行软删除(UPDATE SET is_deleted = 1)
permanent_deletion() 始终执行物理删除
排除指定表
pgbatis::set_unlogic_delete_table("sys_log");
原生 SQL 示例
let sql = "SELECT u.name, o.amount FROM users u \
JOIN orders o ON u.id = o.user_id WHERE o.amount > $1";
let rows = pgbatis::query(sql, &[&100]).await?;
let page = pgbatis::query_page(
"SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id",
&[], "amount", true, 1, 10,
).await?;
let row = pgbatis::query_one("SELECT count(*) as cnt FROM users WHERE age > $1", &[&18]).await?;
安全注意事项
- 始终通过
Wrapper 方法构造条件(参数化查询 $n),避免手写 SQL 拼接用户输入
in_array_string 使用字符串拼接,仅在输入可控时使用
set_order_by 会校验 -- 和 ; 字符,防止注入
许可证
MIT