rust使用sqlx示例

文章目录

概要

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

完整代码

rust 复制代码
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 编程语言的异步运行时
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"] }

项目中引入这些包,

rust 复制代码
use std::env;
use async_trait::async_trait;
use sqlx::Error;
use sqlx::mysql::MySqlPool;

用户实体

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

rust 复制代码
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 UserDo 中 new函数是结构体(UserDo)的有参构造方便后面给结构体赋值。

定义仓储接口

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

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

持久化层

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

rust 复制代码
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连接池类型,

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

rust 复制代码
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同级

xml 复制代码
DATABASE_URL=mysql://用户名:密码@localhost/库名

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

rust 复制代码
#[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函数中我进行方法调用

rust 复制代码
#[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,这里需要修改

powershell 复制代码
cargo install sqlx-cli --no-default-features --features native-tls,mysql

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

powershell 复制代码
sqlx migrate add create_users_table 

这里生成的迁移脚本是空的,需要自己手动写

当迁移脚本添加好了之后 执行以下命令运行迁移脚本

powershell 复制代码
 sqlx migrate run

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

源代码下载

免费下载传送门

小结

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

相关推荐
破茧蝴蝶6 小时前
Python分布式爬虫完整教程
开发语言·python
数据库小学妹6 小时前
国产数据库怎么选?兼容、迁移、核心案例与融合架构四道关
数据库·架构·国产数据库·数据库迁移·融合数据库
狗凯之家源码网6 小时前
全新 PHP 轻量客服聊天室源码深度评测
开发语言·php·聊天室系统
远离UE47 小时前
UE5 Substrate Thin Film 源码拆析
开发语言·javascript·ue5
keyipatience7 小时前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
IvorySQL7 小时前
PostgreSQL 日报| PgBouncer 修复三个 CVE 漏洞(10 月 15 日)
数据库·postgresql·区块链
饺子大魔王的男人7 小时前
后台表格、查询、流程总在重复写?我用ToolJet把PostgreSQL直接做成可远程访问的业务系统
数据库·postgresql
Gauss松鼠会8 小时前
高斯数据库(openGauss)的物理存储结构
运维·服务器·数据库·sql·opengauss·经验总结
栈帧之外8 小时前
【C++】STL之set
开发语言·c++