# pgbatis **Repository Path**: buddycoder/pgbatis ## Basic Information - **Project Name**: pgbatis - **Description**: This is personal project. - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-08-12 - **Last Updated**: 2026-06-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 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. 添加依赖 ```toml [dependencies] pgbatis = "0.1" serde = { version = "1.0", features = ["derive"] } ``` ### 2. 定义实体 ```rust use pgbatis::pgmacro::PGCRUD; use serde::{Deserialize, Serialize}; #[derive(PGCRUD, Serialize, Deserialize, Clone, Debug, Default)] pub struct User { pub id: Option, pub name: Option, pub age: Option, } ``` `#[derive(PGCRUD)]` 还会自动生成对应的 `UserColumn` 结构体(如 `UserColumn::Id`、`UserColumn::Name`), 用于在 `Wrapper` 中引用列名,避免硬编码字符串。 ### 3. 初始化连接 ```rust,ignore // 必须在任何数据库操作之前调用 pgbatis::link("127.0.0.1", 5432, "user", "password", "mydb", 10).await?; ``` ### 4. CRUD 操作 ```rust,ignore 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 = 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::( 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::(Wrapper::new().eq(&UserColumn::Id, &"1"), None, None).await?; // ---- 强制物理删除 ---- pgbatis::permanent_deletion::(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::(wrapper, prefix, suffix)` | 删除(默认软删除,排除表则物理删除) | | `permanent_deletion::(wrapper, prefix, suffix)` | 强制物理 DELETE | ### 类型化查询(返回实体) | 函数 | 说明 | |---|---| | `fetch::(wrapper, prefix, suffix)` | 单表查询,返回 `Vec` | | `fetch_one::(wrapper, prefix, suffix)` | 单表查询单条,返回 `T` | | `fetch_page::(wrapper, prefix, suffix)` | 单表分页,返回 `PageT` | | `fetch_with_recoder_field::(…)` | 单表查询,返回 `Vec>` | ### 原生 SQL 查询(返回 HashMap) | 函数 | 说明 | |---|---| | `query(sql, params)` | 查询列表 → `Vec>` | | `query_one(sql, params)` | 查询单条 → `HashMap` | | `query_page(sql, params, order_by, desc, page, size)` | 分页查询 → `PageHash` | | `query_t::(sql, params)` | 查询列表 → `Vec`(SELECT 字段顺序须与实体一致) | | `execute(sql, params)` | 执行非查询 SQL → 影响行数 | ### 存在性检查 | 函数 | 说明 | |---|---| | `check_row_by_column(table, column, value)` | 按列值检查是否存在 | | `check_row_wrap::(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 条件构造器 ```rust,ignore 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::(&OrderColumn::DeletedAt) .is_not_null::(&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()` 始终执行物理删除 ### 排除指定表 ```rust,ignore // 对日志表执行物理删除,查询时也不过滤 is_deleted pgbatis::set_unlogic_delete_table("sys_log"); ``` ## 原生 SQL 示例 ```rust,ignore // 联表查询 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