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` 提供 `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<String>,
    pub name: Option<String>,
    pub age: Option<i32>,
}
```

`#[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<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 条件构造器


```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::<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()` 始终执行物理删除

### 排除指定表


```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