域名 AXUM.RS 将于2025年10月到期。我们无意再对其进行续费,我们希望你能够接续这个域名,让更多 AXUM 开发者继续受益。
  • 方案1️⃣AXUM.RS 域名 = 3000
  • 方案2️⃣方案1️⃣ + 本站所有专题原始 Markdown 文档 = 5000
  • 方案3️⃣方案2️⃣ + 本站原始数据库 = 5500
如果你有意接续这份 AXUM 情怀,请与我们取得联系。
说明:
  1. 如果有人购买 AXUM.RS 域名(方案1️⃣),或者该域名到期,本站将启用新的免费域名继续提供服务。
  2. 如果有人购买了 AXUM.RS 域名,且同时购买了内容和/或数据库(方案2️⃣/方案3️⃣),本站将关闭。届时我们或许会以另一种方式与你再相遇。

sqlx实现增删改

本章将讨论使用 sqlx 进行增删改(INSERT/DELETE/UPDATE)操作。

如第2章所讲,通过 query()execute(),可以执行非 SELECT 操作,并且它还可以返回最后插入的ID和受影响的行数。

判断是否存在

开始之前,我们需要定义一个函数,用于判断增加或修改的会员是否存在。在数据库中,我们将 name 字段定义成了 unique,所以只需要判断是否存在相同 name 的记录就行了。

// src/db/member.rs

pub async fn exists(conn: &sqlx::MySqlPool, name: &str, id: Option<u32>) -> Result<bool> {
    let sql = "SELECT COUNT(*) FROM member WHERE name=?";
    let with_id_sql: String;

    let q = match id {
        Some(id) => {
            with_id_sql = format!("{} AND id<>?", sql);
            sqlx::query_as(&with_id_sql).bind(&name).bind(id)
        }
        None => sqlx::query_as(sql).bind(&name),
    };

    let count: (i64,) = q.fetch_one(conn).await.map_err(Error::from)?;

    Ok(count.0 > 0)
}

原理很简单,就是通过 SELECT COUNT(*) 来统计指定 name 的记录数,如果这个记录大于0,说明该会员已经存在。

这里有个很奇怪的 id: Option<u32>,它是做什么用的?

  • 对于增加记录来说,只要判断是否存在指定 name的记录就行了
  • 对于修改记录来说,除了要判断是否存在指定 name 的记录,还要判断这个记录是否是它自己。如果不加这个条件,即使不做任何数据的修改,都无法进行操作,exists() 永远返回 true

增加

结合 query()execute(),我们执行了 INSERT 语句,并返回了它最后插入的ID。为什么返回的时候要转成 u32Ok(id as u32))呢?

  • 为了兼容 BIGINT UNSIGNEDlast_insert_id() 返回的是 u64
  • 虽然你可以直接使用 u64 ,但我们的表的主键是 INT UNSIGNED,对应的是 u32。为了统一性,我们还是将它转成了 u32

修改

结合 query()execute(),我们执行了 UPDATE 语句,并返回了受影响的行数。和 INSERT 返回的最后插入ID不同,受影响行数无须转成 u32

// src/db/member.rs

pub async fn del(conn: &sqlx::MySqlPool, id: u32) -> Result<u64> {
    let aff = sqlx::query("UPDATE member SET is_del=true WHERE id=?")
        .bind(id)
        .execute(conn)
        .await
        .map_err(Error::from)?
        .rows_affected();
    Ok(aff)
}

逻辑删除说到底就是将 is_del 字段的值改成 true,它就是一个 UPDATE 操作。

结合 query()execute(),我们执行了 DELETE 语句,并返回了受影响的行数。

本章代码位于04/增删改分支

要查看完整内容,请先登录