Skip to content

Commit 22a72e5

Browse files
committed
update database doc
1 parent 4be895f commit 22a72e5

1 file changed

Lines changed: 92 additions & 51 deletions

File tree

docs/database.md

Lines changed: 92 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# 数据库模块
1+
# 数据库
22

3-
支持 MySQL / PostgreSQL / SQLite 三种数据库的 ORM 和查询功能,通过 Cargo features 开启
3+
支持 MySQL / PostgreSQL / SQLite 数据库的 ORM 和查询功能,基于 [sqlx](https://github.com/launchbadge/sqlx)实现
44

55
## 启用
66

@@ -15,80 +15,103 @@ webr = { version = "0.1", features = ["mysql"] } # 或 "postgres", "sqlite"
1515

1616
```toml
1717
[datasource]
18-
driver = "sqlite"
19-
database = "todos.db"
18+
# SQLite
19+
url = "sqlite://todos.db?mode=rwc"
2020

21-
# 或 PostgreSQL:
22-
# driver = "postgres"
23-
# url = "postgres://user:pass@localhost:5432/mydb"
24-
# host = "localhost"
25-
# port = 5432
26-
# username = "user"
27-
# password = "pass"
21+
# MySQL
22+
# url = "mysql://localhost:3306/db"
23+
# user = "user"
24+
# password = "password"
2825

26+
# PostgreSQL
27+
# url = "postgres://localhost:5432/db"
28+
# user = "user"
29+
# password = "password"
30+
31+
32+
# 连接池配置(可选)
2933
[datasource.pool]
3034
max_connections = 10
3135
min_connections = 0
3236
connect_timeout_secs = 30
3337
idle_timeout_secs = 600
3438
```
3539

36-
如果配置了 `url`,则直接使用完整连接字符串;否则根据 `driver` + 各字段拼接。
37-
3840
## 初始化连接池
3941

42+
### 手动初始化
43+
4044
```rust
4145
use webr::db::{DbPool, DatasourceConfig};
4246

4347
#[webr::main]
4448
async fn main(app: &mut AppBuilder) -> Result<(), Error> {
49+
// 获取数据源配置
4550
let ds_config = app.config()
4651
.get::<DatasourceConfig>("datasource")
4752
.map_err(|e| Error::Internal(e.to_string()))?;
4853

54+
// 创建连接池
4955
let pool = DbPool::from_config(&ds_config).await
5056
.map_err(|e| Error::Database(Box::new(e)))?;
5157

52-
webr::db::set_pool(pool.inner().clone()); // 设置全局池
53-
app.provide(pool)?; // 注册到 DI 容器
58+
// 设置全局池
59+
webr::db::set_pool(pool.inner().clone());
60+
61+
// 注册到容器
62+
app.provide(pool)?;
63+
5464
Ok(())
5565
}
5666
```
5767

58-
或者启用 `auto-init` feature 自动初始化:
68+
### 自动初始化
69+
70+
启用 `auto-init` feature 自动初始化连接池。
5971

6072
```toml
6173
webr = { features = ["sqlite", "auto-init"] }
6274
```
6375

64-
`auto-init` 自动检测 `[datasource]` 配置节,自动创建连接池并注册到 DI 容器。
76+
`auto-init` 自动检测 `[datasource]` 配置节,自动创建连接池并注册到容器。
77+
78+
## `#[entity]`
6579

66-
## #[entity] 实体定义
80+
标记一个`struct`为数据库实体,自动生成CRUD等关联函数。
6781

6882
```rust
6983
#[webr::entity(table = "todos")]
7084
#[derive(Debug, Clone, Serialize, Deserialize)]
7185
pub struct Todo {
72-
#[column(pk)] // 标记主键
86+
// 标记主键
87+
#[column(pk)]
7388
pub id: i64,
7489
pub title: String,
7590
pub done: bool,
7691
}
7792
```
7893

79-
`#[entity]` 宏自动生成:
80-
- `Iden` 枚举(用于 sea-query 构建查询)
81-
- CRUD 方法:`find_all()`, `find_by_id()`, `save()`, `delete()`
82-
- 字段属性:`#[column(pk)]` 主键、`#[column(name = "col")]` 自定义列名
94+
`#[entity]` 宏自动生成以下函数:
95+
96+
| 函数 | 返回值 | 说明 |
97+
|--------------------------------|------------------------|---------------------|
98+
| `find_by_id(id: &PkType)` | `Result<Option<Self>>` | 按主键查询单条记录 |
99+
| `find_all()` | `Result<Vec<Self>>` | 查询全部记录 |
100+
| `find_page(pager: Pagination)` | `Result<Page<Self>>` | 分页查询 |
101+
| `save(&self)` | `Result<()>` | 插入实体,忽略 `None` 字段 |
102+
| `save_batch(items: &[Self])` | `Result<u64>` | 批量插入,生成单条 INSERT 语句 |
103+
| `update(&self)` | `Result<bool>` | 按主键更新,只更新 `Some` 字段 |
104+
| `delete(&self)` | `Result<bool>` | 按主键删除 |
105+
| `count()` | `Result<i64>` | 统计总记录数 |
83106

84107
### CRUD 示例
85108

86109
```rust
87-
// 查询全部(自动使用全局池)
110+
// 查询全部
88111
let todos = Todo::find_all().await?;
89112

90113
// 按 ID 查询
91-
let todo = Todo::find_by_id(&42).await?;
114+
let todo = Todo::find_by_id(42).await?;
92115

93116
// 保存(INSERT + 返回完整记录)
94117
let saved = todo.save().await?;
@@ -97,7 +120,7 @@ let saved = todo.save().await?;
97120
let deleted = todo.delete().await?;
98121
```
99122

100-
## #[sql] 动态查询
123+
## #[sql]
101124

102125
支持 MyBatis 风格的动态 SQL 标签。
103126

@@ -133,7 +156,7 @@ pub async fn search(
133156
}
134157
```
135158

136-
**`<where>`**自动处理 WHERE 关键字,去除多余的 AND/OR
159+
**`<where>`**条件查询
137160

138161
```rust
139162
// 当 title = None, done = Some(true) 时生成:
@@ -175,7 +198,26 @@ pub async fn search_sorted(
175198
}
176199
```
177200

178-
**`<trim>`** — 自定义前缀后缀修整。
201+
**`<trim>`** — 自定义前缀/后缀,并自动去除多余关键字:
202+
203+
```rust
204+
#[sql(r#"
205+
UPDATE todos
206+
<trim prefix="SET" suffixOverrides=",">
207+
<if test="title">title = #{title},</if>
208+
<if test="done">done = #{done},</if>
209+
</trim>
210+
WHERE id = #{id}
211+
"#)]
212+
pub async fn update_optional(
213+
pool: &webr::db::DbPool,
214+
id: i64,
215+
title: Option<&str>,
216+
done: Option<bool>,
217+
) -> Result<()> {
218+
unreachable!()
219+
}
220+
```
179221

180222
### 自定义返回类型
181223

@@ -203,7 +245,7 @@ pub async fn list_tuples(pool: &webr::db::DbPool) -> Result<Vec<(i64, String)>>
203245

204246
### 分页查询
205247

206-
使用 `Pagination` 参数进行分页
248+
使用 `Pagination` 参数进行分页
207249

208250
```rust
209251
use webr::db::Pagination;
@@ -225,25 +267,24 @@ pub async fn search_page(
225267

226268
// 使用
227269
let pager = Pagination::new(1, 20);
228-
let page = Todo::search_page(&pool, Some("rust"), pager).await?;
229-
// page.items, page.total, page.page, page.page_size, page.total_pages, page.has_next, page.has_prev
270+
let page = Todo::search_page( & pool, Some("rust"), pager).await?;
230271
```
231272

232273
`Page<T>` 字段:
233274

234-
| 字段 | 类型 | 说明 |
235-
|------|------|------|
236-
| items | Vec\<T\> | 当前页数据 |
237-
| total | i64 | 总记录数 |
238-
| page | u64 | 当前页码 |
239-
| page_size | u64 | 每页条数 |
240-
| total_pages | u64 | 总页数 |
241-
| has_next | bool | 是否有下一页 |
242-
| has_prev | bool | 是否有上一页 |
275+
| 字段 | 类型 | 说明 |
276+
|-------------|----------|--------|
277+
| items | Vec\<T\> | 当前页数据 |
278+
| total | i64 | 总记录数 |
279+
| page | u64 | 当前页码 |
280+
| page_size | u64 | 每页条数 |
281+
| total_pages | u64 | 总页数 |
282+
| has_next | bool | 是否有下一页 |
283+
| has_prev | bool | 是否有上一页 |
243284

244-
## #[tx] 事务管理
285+
## 事务
245286

246-
### 声明式事务
287+
### `#[tx]` 声明式事务
247288

248289
在 impl block 上标注 `#[tx]`,其下所有 `async fn` 自动包装在事务中:
249290

@@ -282,10 +323,10 @@ impl TodoService {
282323
```rust
283324
use webr::db::{DbTransaction, scope_txn, try_get_txn};
284325

285-
let txn = DbTransaction::begin(&pool).await?;
286-
let result = scope_txn(&txn, async {
287-
// 事务中的操作...
288-
Ok::<_, Error>(())
326+
let txn = DbTransaction::begin( & pool).await?;
327+
let result = scope_txn( & txn, async {
328+
// 事务中的操作...
329+
Ok::<_, Error>(())
289330
}).await;
290331
txn.commit().await?; // 或 txn.rollback().await?;
291332
```
@@ -294,17 +335,17 @@ txn.commit().await?; // 或 txn.rollback().await?;
294335

295336
```rust
296337
// fetch_all: 查询多行
297-
pool.fetch_all::<Todo>("SELECT * FROM todos WHERE done = ?", |b| b.bind(false)).await?;
338+
pool.fetch_all::<Todo>("SELECT * FROM todos WHERE done = ?", | b| b.bind(false)).await?;
298339

299340
// fetch_optional: 查询可选单行
300-
pool.fetch_optional::<Todo>("SELECT * FROM todos WHERE id = ?", |b| b.bind(42)).await?;
341+
pool.fetch_optional::<Todo>("SELECT * FROM todos WHERE id = ?", | b| b.bind(42)).await?;
301342

302343
// fetch_one: 查询确切一行(无数据则报错)
303-
pool.fetch_one::<Todo>("SELECT * FROM todos WHERE id = ?", |b| b.bind(42)).await?;
344+
pool.fetch_one::<Todo>("SELECT * FROM todos WHERE id = ?", | b| b.bind(42)).await?;
304345

305346
// execute: INSERT/UPDATE/DELETE,返回影响行数
306-
pool.execute("UPDATE todos SET done = ? WHERE id = ?", |b| b.bind(true).bind(42)).await?;
347+
pool.execute("UPDATE todos SET done = ? WHERE id = ?", | b| b.bind(true).bind(42)).await?;
307348

308349
// fetch_scalar: 标量查询
309-
let count: i64 = pool.fetch_scalar("SELECT COUNT(*) FROM todos", |b| b).await?;
350+
let count: i64 = pool.fetch_scalar("SELECT COUNT(*) FROM todos", | b| b).await?;
310351
```

0 commit comments

Comments
 (0)