sqlx概览
开始之前,我们需要一些准备工作,包括:创建用于演示的数据库及数据、创建一个 Rust 项目以及为项目编写一些基础性代码。sqlx的查询构造器
本章将讨论 sqlx 的查询构造器。sqlx 提供了多种查询构造器:`query`、`query_as`、`query!`、`query_as!`、`QueryBuilder`等,合理使用将提升效率。这些查询构造器用来生成数据库所需要的 SQL 语句,不要被它们的名字迷惑了——认为它们是用来查询数据(SQL中的`SELECT`行为)sqlx查询数据
本章将讨论使用 sqlx 执行 `SELECT` 语句,对数据进行查询。sqlx实现增删改
本章将讨论使用 sqlx 进行增删改(`INSERT/DELETE/UPDATE`)操作。使用sqlx的事务实现转账
本章我们将通过用户之间转账来讨论 sqlx 的事务。为了保证转账的完整性、正确性,我们必须使用事务来处理。使用sqlx的QueryBuilder构建复杂、动态的查询
本章我们讨论如何优雅方便地使用 sqlx 构建复杂的、动态的 SQL。sqlx 提供了 [`QueryBuilder`](https://docs.rs/sqlx/latest/sqlx/struct.QueryBuilder.html) 结构体,它可以方便地实现 SQL 的构建。sqlx优雅地实现IN查询
在日常开发中,`IN` 查询是非常常见的需求,你会怎么来处理呢?借助 sqlx 的`QueryBuilder` 可以方便地实现。
sqlx实现增删改
本章将讨论使用 sqlx 进行增删改(INSERT/DELETE/UPDATE
)操作。
判断是否存在
开始之前,我们需要定义一个函数,用于判断增加或修改的会员是否存在。在数据库中,我们将 name
字段定义成了 unique
,所以只需要判断是否存在相同 name
的记录就行了。
原理很简单,就是通过 SELECT COUNT(*)
来统计指定 name
的记录数,如果这个记录大于0,说明该会员已经存在。
这里有个很奇怪的 id: Option<u32>
,它是做什么用的?
- 对于增加记录来说,只要判断是否存在指定
name
的记录就行了 - 对于修改记录来说,除了要判断是否存在指定
name
的记录,还要判断这个记录是否是它自己。如果不加这个条件,即使不做任何数据的修改,都无法进行操作,exists()
永远返回true
增加
// src/db/member.rs
pub async fn add(conn: &sqlx::MySqlPool, m: &model::member::Member) -> Result<u32> {
if exists(conn, &m.name, None).await? {
return Err(Error::exists("同名的会员已存在"));
}
let id = sqlx::query(
"INSERT INTO `member` (name, dateline, balance, types,is_del) VALUES(?,?,?,?,?)",
)
.bind(&m.name)
.bind(&m.dateline)
.bind(&m.balance)
.bind(&m.types)
.bind(&m.is_del)
.execute(conn)
.await
.map_err(Error::from)?
.last_insert_id();
Ok(id as u32)
}
结合 query()
和 execute()
,我们执行了 INSERT
语句,并返回了它最后插入的ID。为什么返回的时候要转成 u32
(Ok(id as u32)
)呢?
- 为了兼容
BIGINT UNSIGNED
,last_insert_id()
返回的是u64
- 虽然你可以直接使用
u64
,但我们的表的主键是INT UNSIGNED
,对应的是u32
。为了统一性,我们还是将它转成了u32
。
修改
// src/db/member.rs
pub async fn edit(conn: &sqlx::MySqlPool, m: &model::member::Member) -> Result<u64> {
if exists(conn, &m.name, Some(m.id)).await? {
return Err(Error::exists("同名的会员已存在"));
}
let aff = sqlx::query("UPDATE `member` SET name=?,balance=?,types=? WHERE id=?")
.bind(&m.name)
.bind(&m.balance)
.bind(&m.types)
.bind(&m.id)
.execute(conn)
.await
.map_err(Error::from)?
.rows_affected();
Ok(aff)
}
结合 query()
和 execute()
,我们执行了 UPDATE
语句,并返回了受影响的行数。和 INSERT
返回的最后插入ID不同,受影响行数无须转成 u32
。
逻辑删除说到底就是将 is_del
字段的值改成 true
,它就是一个 UPDATE
操作。
物理删除
// src/db/member.rs
pub async fn real_del(conn: &sqlx::MySqlPool, id: u32) -> Result<u64> {
let aff = sqlx::query("DELETE FROM member WHERE id=?")
.bind(id)
.execute(conn)
.await
.map_err(Error::from)?
.rows_affected();
Ok(aff)
}
本章代码位于04/增删改分支