本章将讨论 sqlx 的查询构造器。sqlx 提供了多种查询构造器:query
、query_as
、query!
、query_as!
、QueryBuilder
等,合理使用将提升效率。这些查询构造器用来生成数据库所需要的 SQL 语句,不要被它们的名字迷惑了——认为它们是用来查询数据(SQL中的SELECT
行为)
query()
函数
官方文档:
fn.query
它是最简单的查询构造器,它接收的参数就是 SQL 语句,返回的是 Query
结构体的实例。比如:
// 示例1
sqlx::query('SELECT * FROM member');
// 示例2
sqlx::query('SELECT * FROM member WHERE id=?');
// postgre
sqlx::query('SELECT * FROM member WHERE id=$1');
// 示例3
sqlx::query('INSERT INTO member (name, balance) VALUES (?,?)');
// postgre
sqlx::query('INSERT INTO member (name, balance) VALUES ($1,$2)');
// 示例4
sqlx::query('UPDATE member SET name=? WHERE id=?');
// postgre
sqlx::query('UPDATE member SET name=$1 WHERE id=$2');
query_as()
函数
官方文档:
fn.query_as
该函数主要用于 SELECT
操作,因为它会将 SQL 执行结果自动映射为 rust 表达式的左值,比如最常用的,将 SELECT
结果映射为结构体。
// 示例1
let member: model::Member = sqlx::query_as('SELECT * FROM member LIMIT 1');
// 示例2
let count: (i64, ) = sqlx::query_as('SELECT COUNT(*) FROM member');
如果数据库中,某些字段允许为空(即没有
NOT NULL
约束),那么结构体中对应字段需要加上Option<>
- 示例1:将查询结果映射到
model::Member
结构体 - 示例2:将查询结果映射到
(i64,)
——它是 sqlx 的QueryScalar
结构体,主要用于COUNT(*)
等简单数据的映射,详情请查看QueryScalar
的文档。
query!
和 query_as!
宏
和同名的函数作用类似,不同之处在于,宏是在编译期间生成代码、检查错误的。从运行效率上来说,宏会优于同名函数,但宏也有要求:
- 依赖中,需要开启
macros
- 必须定义环境变量
DATABASE_URL
,该环境变量用于指定数据库连接地址。
我们目前不使用宏,主要原因是开发工具不具备对宏的提示功能,会造成开发效率低下。
QueryBuilder
结构体QueryBuilder
用于构建复杂的、动态的查询。我们将在后面的章节里专门进行讨论
常用方法
使用 query()
和 query_as()
构建好 SQL 之后,还需要调用(支持链式调用)一些常用方法来进一步处理。
bind()
方法
用于绑定参数。在 MySQL 中,SQL 语句中每一个 ?
一一对应 bind()
;在 postgre 中,每一个 $N
(N
为从1开始的数字)一一对应bind()
:
// 示例1
let id:u32 = 1;
sqlx::query('SELECT * FROM member WHERE id=?').bind(id);
// postgre
sqlx::query('SELECT * FROM member WHERE id=$1').bind(id);
// 示例2
let m = model::Member{
name: String::from("axum中文网"),
balance: 123,
..Default::default()
};
sqlx::query('INSERT INTO member (name, balance) VALUES (?,?)').bind(&m.name).bind(m.balance);
// postgre
sqlx::query('INSERT INTO member (name, balance) VALUES ($1,$2)').bind(&m.name).bind(m.balance);
// 示例3
let m = model::Member{
id: 1,
name: String::from("axum中文网 axum.rs"),
..Default::default()
};
sqlx::query('UPDATE member SET name=? WHERE id=?').bind(&m.name).bind(m.id);
// postgre
sqlx::query('UPDATE member SET name=$1 WHERE id=$2').bind(&m.name).bind(m.id);
文档:method.bind
fetch_one()
方法
用于查询单条记录,通常配合 query_as
使用。如果记录不存在,则返回错误。
// 示例1
let m: model::Member = sqlx::query_as('SELECT * FROM member WHERE id=? AND is_del=? LIMIT 1')
.bind(id)
.bind(false)
.fetch_one(&conn)
.await
.unwrap();
// 示例2
let count:(i64,) = sqlx::query_as('SELECT COUNT(*) FROM member WHERE is_del=?')
.bind(false)
.fetch_one(&conn)
.await
.unwrap();
fetch_optional()
方法
和 fetch_one()
类似,也是用来查询单条记录。不同之处在于,如果记录不存在,返回 None
:
let m: Option<model::Member> = sqlx::query_as('SELECT * FROM member WHERE id=? AND is_del=? LIMIT 1')
.bind(id)
.bind(false)
.fetch_optional(&conn)
.await
.unwrap();
fetch_all()
方法
用于查询多条记录。
let m: Vec<model::Member> = sqlx::query_as('SELECT * FROM member WHERE is_del=? ORDER BY id DESC')
.bind(false)
.fetch_all(&conn)
.await
.unwrap();
execute()
方法
通常用于执行非 SELECT
语句:INSERT/UPDATE/DELETE
。它的返回值是QueryResult
,它有两个常用方法:
last_insert_id()
:返回最后插入的IDrows_affected
:返回受影响的行数
注意,postgre 不会自动在
INSERT
后返回自动编号的ID,所以需要配合fetch_one()
和 SQL 中的RETURNING id
来获取最后插入的ID
conn
是什么?
上面例中,多次出现了 &conn
,它是什么?它是一个名为 Executor
的 trait,通常是:
- 连接池的引用,比如
&MySqlPool
。 - 事务的引用,比如
&mut Transaction
。后续章节会专门讨论事务。