概要

rust使用sqlx对mysql数据库进行添加操作示例,以及sqlx cli使用方法(sqlx cli是sqlx的数据管理工具提供数据库管理和迁移支持)

完整代码

use std::env;
use async_trait::async_trait;
use sqlx::Error;
use sqlx::mysql::MySqlPool;

pub  struct UserDo {
    pub id:u64,
    pub username:String,
    pub email:String,
    pub password:String,

}
impl UserDo {
    pub fn new (username:String, email:String, password:String) -> Self {
        Self{
            id:0,
            username,
            email,
            password
        }
    }
}
#[async_trait]
 pub  trait  UserRepo: Send + Sync {
     async fn  add_user(&self, _: UserDo) ->Result<UserDo,Error>;
 }
pub  struct UserPersistence {
    pub  pool:MySqlPool,
}
impl UserPersistence  {
    pub async fn new() -> Result<Self,Error> {
        let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
       println!("DATABASE_URL: {}", &database_url);
        let pool = MySqlPool::connect(&database_url).await?;
        Ok(Self { pool })
    }
}
#[async_trait]
 impl UserRepo for UserPersistence {
    async  fn add_user(&self, user: UserDo) -> Result<UserDo, Error>{
        let result = sqlx::query!(
            "INSERT INTO users (username, email, password) VALUES (?, ?, ?)",
            user.username,
            user.email,
            user.password
        )
            .execute(&self.pool)
            .await?;

        let user_id = result.last_insert_id();

        let inserted_user: UserDo = sqlx::query_as!(
            UserDo,
            "SELECT id, username, email, password FROM users WHERE id = ?",
            user_id
        )
            .fetch_one(&self.pool)
            .await?;

        Ok(inserted_user)
    }
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    dotenv::dotenv().ok();
    // 初始化数据库连接池
    let user_service = UserPersistence::new().await?;

    // 创建新用户对象
    let user_do = UserDo::new("sssacsa".into(), "889999@qq.com".into(), "opass".into());

    // 添加用户并处理结果
    match user_service.add_user(user_do).await {
        Ok(inserted_user) => {
            println!("User added successfully: ID {}", inserted_user.id);
            Ok(())
        }
        Err(e) => {
            eprintln!("Failed to add user: {}", e);
            Err(e.into())
        }
    }
}

项目结构

在这里插入图片描述

运行效果

项目运行效果

执行命令

cargo run

在这里插入图片描述

执行结果展示

在这里插入图片描述

项目介绍

包引用介绍

该示例主要用到的包就配置文件如下

  • async-trait:提供异步支持
  • dotenv:提供配置文件读取支持
  • sqlx :提供数据库交互支持
  • tokio:Rust 编程语言的异步运行时
[dependencies]
async-trait = "0.1.89"
dotenv = "0.15.0"
sqlx = { version = "0.8.6",features = ["mysql","runtime-async-std-native-tls"] }
tokio = { version = "1.47.1", features = ["rt", "rt-multi-thread", "macros"] }

项目中引入这些包,

use std::env;
use async_trait::async_trait;
use sqlx::Error;
use sqlx::mysql::MySqlPool;

用户实体

以下do实体是用于与数据库交互的实体,需要注意的是在实际企业级应用开发中实体肯定不会这么简单,这里只是为了给初学者示例,方便大家理解所以字段相对较少。

pub  struct UserDo {
    pub id:u64,
    pub username:String,
    pub email:String,
    pub password:String,

}
impl UserDo {
    pub fn new (username:String, email:String, password:String) -> Self {
        Self{
            id:0,
            username,
            email,
            password
        }
    }
}

上面的代码 impl UserDonew函数是结构体(UserDo)的有参构造方便后面给结构体赋值。

定义仓储接口

该接口主要实现对用户这个模块的新增功能

#[async_trait]
 pub  trait  UserRepo: Send + Sync {
     async fn  add_user(&self, _: UserDo) ->Result<UserDo,Error>;
 }

持久化层

持久化层主要是负责对仓储接口的实现

pub  struct UserPersistence {
    pub  pool:MySqlPool,
}
impl UserPersistence  {
    pub async fn new() -> Result<Self,Error> {
        let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
       println!("DATABASE_URL: {}", &database_url);
        let pool = MySqlPool::connect(&database_url).await?;
        Ok(Self { pool })
    }
}
#[async_trait]
 impl UserRepo for UserPersistence {
    async  fn add_user(&self, user: UserDo) -> Result<UserDo, Error>{
        let result = sqlx::query!(
            "INSERT INTO users (username, email, password) VALUES (?, ?, ?)",
            user.username,
            user.email,
            user.password
        )
            .execute(&self.pool)
            .await?;

        let user_id = result.last_insert_id();

        let inserted_user: UserDo = sqlx::query_as!(
            UserDo,
            "SELECT id, username, email, password FROM users WHERE id = ?",
            user_id
        )
            .fetch_one(&self.pool)
            .await?;

        Ok(inserted_user)
    }
}

上面这段代码有点多,不用急咱们分段来解释
首先这段代码,这里可以看到我们也是定义了一个结构体实现了它的构造方法
但是与上面不同的是我们的构造方法并没有传入参数,这里其实我们读取的是环境变量,.env文件。这里使用到的是rust的一个插件,dotenv;你可以把这里理解为像C#读取appsetting.json文件一样。
MySqlPool是一个sqlx提供的mysql连接池类型,

它的作用就是读取到配置文件中的数据库连接字符串。创建连接池

pub  struct UserPersistence {
    pub  pool:MySqlPool,
}
impl UserPersistence  {
    pub async fn new() -> Result<Self,Error> {
        let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
       println!("DATABASE_URL: {}", &database_url);
        let pool = MySqlPool::connect(&database_url).await?;
        Ok(Self { pool })
    }
}

注意:.env文件文件是自己创建的文件,与Cargo.toml同级
在这里插入图片描述

DATABASE_URL=mysql://用户名:密码@localhost/库名

下面这段代码是实现上面定义的用户仓储实现持久化到数据库中,传入的是一个UserDo实体,返回值是新插入的这个UserDo,如果报错的话会返回Error信息

#[async_trait]
 impl UserRepo for UserPersistence {
    async  fn add_user(&self, user: UserDo) -> Result<UserDo, Error>{
        let result = sqlx::query!(
            "INSERT INTO users (username, email, password) VALUES (?, ?, ?)",
            user.username,
            user.email,
            user.password
        )
            .execute(&self.pool)
            .await?;

        let user_id = result.last_insert_id();

        let inserted_user: UserDo = sqlx::query_as!(
            UserDo,
            "SELECT id, username, email, password FROM users WHERE id = ?",
            user_id
        )
            .fetch_one(&self.pool)
            .await?;

        Ok(inserted_user)
    }
}

应用层

在main函数中我进行方法调用

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    dotenv::dotenv().ok();
    // 初始化数据库连接池
    let user_service = UserPersistence::new().await?;

    // 创建新用户对象
    let user_do = UserDo::new("sssacsa".into(), "889999@qq.com".into(), "opass".into());

    // 添加用户并处理结果
    match user_service.add_user(user_do).await {
        Ok(inserted_user) => {
            println!("User added successfully: ID {}", inserted_user.id);
            Ok(())
        }
        Err(e) => {
            eprintln!("Failed to add user: {}", e);
            Err(e.into())
        }
    }
}

接下来咱们来解释一下上面这段代码:

dotenv::dotenv().ok();

这句代码看起来很突兀对吧,但是它是不能缺少的,它的作用是把 .env 文件里的 KEY=value 逐行读出来,塞进当前进程的「环境变量表」里,之后 std::env::var(“KEY”) 就能拿到值。如果这段代码没有我们上面读取数据库连接字符串哪里就会报错。

match user_service.add_user(user_do).await...

这段代码的作用是接受方法返回值,还记得我们add_user函数中使用的返回类型是什么吗?是Result<UserDo, Error>;Result有两个变体,一个是用户实体,一个是错误实体。
我们使用match表达式就相当于c# 中的if判断,
如果方法执行成功了咱们再Ok里就接收它的返回值,然后打印,如果失败了就程序panic直接恐慌,程序直接死给你看然后把错误信息给提示出来。

sqlx cli的使用

安装

首先执行进行安装;注意:这里我使用的是mysql所以代码最后填写的是mysql,如果你使用的是pgsql,这里需要修改

cargo install sqlx-cli --no-default-features --features native-tls,mysql

当安装完成后我们可以在项目控制台执行迁移命令;当执行完成之后会在项目中生成一个migrations文件夹,里面有我们创建的迁移脚本create_users_table ;注意:create_users_table 这个是可以自定义的迁移脚本文件名。

sqlx migrate add create_users_table 

在这里插入图片描述

这里生成的迁移脚本是空的,需要自己手动写
在这里插入图片描述
当迁移脚本添加好了之后 执行以下命令运行迁移脚本

 sqlx migrate run

执行结束后我们可以在库里看到有一点变化,sqlx会创建一个迁移记录表
在这里插入图片描述
在这里插入图片描述

源代码下载

免费下载传送门

小结

以上就是我今天给大家分享sqlx的简单使用示例,希望对大家有所帮助。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐