使用sqlx的事务实现转账

事务的常用操作

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

实现转账

开启事务

let mut tx = conn.begin().await.map_err(Error::from)?;
  • 事务必须由 mut 修饰
  • &sqlx::MySqlPoolbegin() 方法可以开启一个事务
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));
    }
};
  • 增加转入账户的余额
  • 通过 match 来处理不同的情况,逻辑和转出扣款一致,不再重复
  • 同样注意 execute() 应该使用事务,而不是连接池
  • 你也可以增加 if to_aff < 1 的处理逻辑,这里就当是作业留给你完成

提交事务

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

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

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

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