sqlx的查询构造器

882
2023/06/04 14:29:04

本章将讨论 sqlx 的查询构造器。sqlx 提供了多种查询构造器:queryquery_asquery!query_as!QueryBuilder等,合理使用将提升效率。这些查询构造器用来生成数据库所需要的 SQL 语句,不要被它们的名字迷惑了——认为它们是用来查询数据(SQL中的SELECT行为)

query() 函数

官方文档:fn.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 结果映射为结构体。

它有一个重要前提:被映射的对象,必须实现 sqlx::FromRow。它的返回值是QueryAs结构体。

// 示例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()

文档: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();

文档:method.fetch_one

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();

文档:method.fetch_optional

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();

文档:method.fetch_all

execute() 方法

文档:method.execute

通常用于执行非 SELECT 语句:INSERT/UPDATE/DELETE。它的返回值是QueryResult,它有两个常用方法:

注意,postgre 不会自动在 INSERT 后返回自动编号的ID,所以需要配合 fetch_one() 和 SQL 中的 RETURNING id 来获取最后插入的ID

// 示例1
let m = model::Member{
    name: String::from("axum中文网"),
    balance: 123,
    ..Default::default()
};
let id = sqlx::query('INSERT INTO member (name, balance) VALUES (?,?)')
	.bind(&m.name)
	.bind(m.balance)
	.execute(&conn).await.unwrap()
	.last_insert_id();

// 示例2
let m = model::Member{
    id: 1,
    name: String::from("axum中文网 axum.rs"),
    ..Default::default()
};
let aff = sqlx::query('UPDATE member SET name=? WHERE id=?')
	.bind(&m.name)
	.bind(m.id)
	.execute(&conn).await.unwrap()
	.rows_affected();

// 示例3
let aff = sqlx::query('DELETE member WHERE id=?')
	.bind(id)
	.execute(&conn).await.unwrap()
	.rows_affected();

conn 是什么?

上面例中,多次出现了 &conn,它是什么?它是一个名为 Executor 的 trait,通常是:

  • 连接池的引用,比如 &MySqlPool
  • 事务的引用,比如&mut Transaction。后续章节会专门讨论事务