CRUD 操作
查询与执行
CRUD 操作 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 4 节课。
使用 sqlx 实现 CRUD
CRUD 代表创建、读取、更新和删除。sqlx 将这些操作分为两种执行方式:
fetch_*用于返回行的查询execute用于修改数据的语句
创建(INSERT)
使用绑定参数插入一行,并通过 execute 执行。使用 RETURNING 获取生成的 id。
use sqlx::{Pool, Postgres};
async fn create(pool: &Pool<Postgres>, name: &str) -> i32 {
let rec = sqlx::query!(
"INSERT INTO users (name) VALUES ($1) RETURNING id",
name
).fetch_one(pool).await.unwrap();
rec.id
}读取一行
fetch_one 要求恰好返回一行,否则会报错。当该行可能不存在时,请使用 fetch_optional。
use sqlx::{Pool, Postgres};
async fn find(pool: &Pool<Postgres>, id: i32) -> Option<String> {
let rec = sqlx::query!("SELECT name FROM users WHERE id = $1", id)
.fetch_optional(pool)
.await
.unwrap();
rec.map(|r| r.name)
}读取多行
fetch_all 返回所有匹配行组成的 Vec。
use sqlx::{Pool, Postgres};
async fn all_names(pool: &Pool<Postgres>) -> Vec<String> {
let rows = sqlx::query!("SELECT name FROM users ORDER BY id")
.fetch_all(pool)
.await
.unwrap();
rows.into_iter().map(|r| r.name).collect()
}更新
UPDATE 语句使用 execute,它会返回一个结果,其中包含受影响的行数。
use sqlx::{Pool, Postgres};
async fn rename(pool: &Pool<Postgres>, id: i32, name: &str) -> u64 {
let res = sqlx::query!("UPDATE users SET name = $1 WHERE id = $2", name, id)
.execute(pool)
.await
.unwrap();
res.rows_affected()
}删除
DELETE 同样使用 execute。检查 rows_affected(),即可知道是否删除了任何内容。
use sqlx::{Pool, Postgres};
async fn delete(pool: &Pool<Postgres>, id: i32) -> bool {
let res = sqlx::query!("DELETE FROM users WHERE id = $1", id)
.execute(pool)
.await
.unwrap();
res.rows_affected() > 0
}使用纯 Rust 模拟 CRUD
针对内存映射执行 CRUD,与 sqlx 针对数据表执行的相同操作相对应。
use std::collections::HashMap;
fn main() {
let mut users: HashMap<i32, String> = HashMap::new();
users.insert(1, "Alice".to_string()); // create
println!("{:?}", users.get(&1)); // read
users.insert(1, "Alicia".to_string()); // update
println!("{:?}", users.get(&1));
users.remove(&1); // delete
println!("{:?}", users.get(&1));
}事务
使用事务以原子方式组合多个语句。所有语句要么在 commit 时全部成功,要么全部不生效。
use sqlx::{Pool, Postgres};
async fn transfer(pool: &Pool<Postgres>) {
let mut tx = pool.begin().await.unwrap();
sqlx::query!("UPDATE acct SET bal = bal - 10 WHERE id = 1")
.execute(&mut *tx).await.unwrap();
sqlx::query!("UPDATE acct SET bal = bal + 10 WHERE id = 2")
.execute(&mut *tx).await.unwrap();
tx.commit().await.unwrap();
}回滚
如果事务保护对象在未调用 commit 的情况下被丢弃,sqlx 会自动回滚。您也可以显式调用 rollback。
use sqlx::{Pool, Postgres};
async fn maybe(pool: &Pool<Postgres>, ok: bool) {
let mut tx = pool.begin().await.unwrap();
sqlx::query!("INSERT INTO log (msg) VALUES ('x')").execute(&mut *tx).await.unwrap();
if ok { tx.commit().await.unwrap(); } else { tx.rollback().await.unwrap(); }
}流式读取大量数据
对于非常大的结果集,fetch 会返回一个流,让您可以逐行处理,而无需将所有数据加载到内存中。
use sqlx::{Pool, Postgres};
use futures::TryStreamExt;
async fn stream(pool: &Pool<Postgres>) {
let mut rows = sqlx::query!("SELECT id FROM big_table").fetch(pool);
while let Some(row) = rows.try_next().await.unwrap() {
println!("id {}", row.id);
}
}处理错误
数据库调用会返回 Result。在返回 Result<_, sqlx::Error> 的函数中,请使用 ? 传播错误,而不要使用解包操作。
use sqlx::{Pool, Postgres};
async fn count(pool: &Pool<Postgres>) -> Result<i64, sqlx::Error> {
let rec = sqlx::query!("SELECT COUNT(*) as n FROM users")
.fetch_one(pool)
.await?;
Ok(rec.n.unwrap_or(0))
}快速检查
检验您对 CRUD 操作的理解。
回顾
您已经学习了使用 sqlx 实现 CRUD:
fetch_one、fetch_optional和fetch_all用于读取行execute用于修改数据,并报告rows_affected()RETURNING用于获取生成的值- 使用
begin/commit/rollback的事务可确保原子性
下一步:使用迁移演进架构。
常见问题解答
「CRUD 操作」课时是免费的吗?
是的 — 「CRUD 操作」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 4 节课。
「CRUD 操作」这节课中我会学到什么?
查询与执行 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Learn Rust Coding 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「CRUD 操作」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Learn Rust Coding 课中编写并运行代码吗?
能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。