pgbatis 0.1.51

pgbaits 用于操作数据库数据的增删改查
Documentation

pgbatis

pgbaits 编写规则纯属个人习惯与风格,不喜勿用。

轻量级异步 PostgreSQL CRUD 辅助库,基于 tokio-postgres + deadpool-postgres

通过派生宏 #[derive(PGCRUD)] 快速为结构体生成增删改查能力,提供链式条件构造器(Wrapper)和可选逻辑删除支持。

特性

  • 派生宏驱动#[derive(PGCRUD)] 自动生成 INSERT/UPDATE/SELECT SQL 及 Parameters trait 实现
  • 链式条件构造Wrapper 提供 eqlikebetweengtset_pages 等链式方法,统一使用 $n 参数化查询
  • 逻辑删除 — 默认启用,读操作自动过滤已删除数据,remove() 默认软删除,支持按表排除
  • 分页查询 — 单表 fetch_page 和原生 SQL query_page 均自动计算 total、pages
  • 事务支持transaction_delete_and_savetransaction_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::IdUserColumn::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;

// 假设实体 Order 有字段 status, price, stock, created_at 等
// PGCRUD 会生成对应的 OrderColumn
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_order_by("id", false)?                     // 字符串形式,含注入校验
    // 分页
    .set_pages(1, 20);

// 构建 WHERE 子句
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() 始终执行物理删除

排除指定表

// 对日志表执行物理删除,查询时也不过滤 is_deleted
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