域名 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 的事务。为了保证转账的完整性、正确性,我们必须使用事务来处理。

事务的常用操作

  • 开启事务:conn.begin().await
  • 提交事务:tx.commit().await
  • 回滚事务:tx.rollback().await

实现转账

// src/db/member.rs

pub async fn tran(conn: &sqlx::MySqlPool, t: &model::member::Tran) -> Result<(u64, u64)> {
    let mut tx = conn.begin().await.map_err(Error::from)?;

    let from_aff =
        match sqlx::query("UPDATE member SET balance=balance-? WHERE name=? AND balance>=?")
            .bind(&t.amount)
            .bind(&t.from_member)
            .bind(&t.amount)
            .execute(&mut tx)
            .await
        {
            Ok(r) => r.rows_affected(),
            Err(err) => {
                tx.rollback().await.map_err(Error::from)?;
                return Err(Error::from(err));
            }
        };

    if from_aff < 1 {
        tx.rollback().await.map_err(Error::from)?;
        return Err(Error::tran("转账失败,请检查转出账户是否有足够余额"));
    }

    let to_aff = match sqlx::query("UPDATE member SET balance=balance+? WHERE name=?")
        .bind(&t.amount)
        .bind(&t.to_member)
        .execute(&mut tx)
        .await
    {
        Ok(r) => r.rows_affected(),
        Err(err) => {
            tx.rollback().await.map_err(Error::from)?;
            return Err(Error::from(err));
        }
    };

    tx.commit().await.map_err(Error::from)?;

    Ok((from_aff, to_aff))
}
  • 事务必须由 mut 修饰
  • &sqlx::MySqlPoolbegin() 方法可以开启一个事务

转出账户扣款

  • 成功时,返回受影响的行数
  • 失败时,回滚事务,并返回错误
  • 账户余额不足
  • 输入的会员名称不存在

增加转入账户的余额

let to_aff = match sqlx::query("UPDATE member SET balance=balance+? WHERE name=?")
    .bind(&t.amount)
    .bind(&t.to_member)
    .execute(&mut tx)
    .await
{
    Ok(r) => r.rows_affected(),
    Err(err) => {
        tx.rollback().await.map_err(Error::from)?;
        return Err(Error::from(err));
    }
};

提交事务

tx.commit().await.map_err(Error::from)?;

只有提交了事务,修改的数据才会真实提交到数据库里。

本章代码位于05/转账-事务分支。

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