Rust使用Sqlx连接Mysql的实现

 更新时间:2024年03月14日 10:44:49   作者:L_Qiu  
数据库在编程中是一个很重要的环节,本文主要介绍了Rust使用Sqlx连接Mysql的实现,记录rust如何操作数据库并以mysql为主的做简单的使用说明,感兴趣的可以了解一下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

数据库在编程中是一个很重要的环节,这里是记录rust如何操作数据库并以mysql为主的做简单的使用说明。rust中对mysql数据库存有支持的我所知道的crate:

  • mysql 单一驱动
  • sqlx 多驱动,异步,不是ORM
  • diesel 多驱动,异常,ORM

因为mysqlcrate有点单一,这里主要是说明sqlxdiesel的使用,分两篇记录。本都吃是以sqlx为说明

在这过程的走了不少不能言语的弯路主要有以下两点:

  • 文档都是英文,多数都是偏向于postgres(英文不好)
  • select出来的数据不直观。不像php那样直接一个关联数据解决所有问题

sqlx的sql是原始的,我所知道的是它不像ORM那样,能通过一系列的方法组合成相应的sql,sql都是手写的。是不是用ORM,看自己的需求进行选择

crate 依赖(Cargo.toml文件)

[dependencies]
tokio = { version = "1", features = ["full"] }
sqlx = { version = "0.6", features = [ 
    "runtime-tokio-native-tls", 
    "mysql", 
    "chrono", 
    "json",
    "macros",
    "decimal"
] }
dotenvy = "0.15.6"
chrono = { version = "0.4.23", features = ["serde"] }
sqlx-cli = "0.6.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
rust_decimal = "1.28.0"

这里我使用的运行时是tokio,它好像支持三种运行时,另外两种是Async-std(runtime-async-std-native-tls),Actix-web(runtime-actix-native-tls),不同的运行里相应的要在sqlx开启相应的特性支持。在这里有一个很重要的特性macros,如果大量使用宏也就是query*!,而且也非常方面查询,可以让我获取类似php中操作数据库的感觉,所以这个特性很重要。

方式1:原始操作方面

通过下标来获取数据

.env文件内容

DATABASE_URL=mysql://root:root@localhost:3306/test

main.rs文件内容

use chrono::{NaiveDateTime};    // 处理 数据库中 datetime 类型
use sqlx::Row;                  // get 方法的 trait
use sqlx::mysql::{MySqlRow, MySqlPoolOptions};
use dotenvy::dotenv;
use std::{env};
use serde::{Deserialize, Serialize};
use serde_json::Value;          // 处理 数据库中 json 类型
use rust_decimal::Decimal;      // 处理 数据库中 decimal 类型

#[derive(Debug)]
struct RawRawData {
    id: i32, 
    title: String,
    subtitle: String,
    content: String,
    tag: Value,
    create_time: NaiveDateTime,
    normal_time: i32,
    vip_pay: Decimal,
    normal_pay: f32,
}

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    dotenv().ok();

    let url = env::var("DATABASE_URL").expect("没有设置数据信息");
    let pool = MySqlPoolOptions::new().connect(&url).await?;
    
    let rows: Vec<MySqlRow> = sqlx::query("select id,title from test").fetch_all(&pool).await?;

    let mut data: Vec<RawRawData> = vec![];

    for row in rows.iter() {
        let id: i32 = row.get(0);
        let title: String = row.get(1);
        let subtitle: String = row.get(2);
        let content: String = row.get(3);
        let tag: Value = row.get(4);
        let create_time: NaiveDateTime = row.get(5);
        let normal_time: i32 = row.get(6);
        let vip_pay: Decimal = row.get(7);
        let normal_pay: f32 = row.get(8);
        data.push(RawRawData{
            id,
            title,
            subtitle,
            content,
            tag,
            create_time,
            normal_time,
            vip_pay,
            normal_pay
        });
    }

    Ok(())
}

单纯的查询出来的 rows 数据打印出来有点初一看看不明白,没有直观性的 key=>value 的形式,感觉是因为强类的原因,不能单纯的当做 String=>String 来处理,所以数据库中的类型也要与rust中的类型相一一对应。上面的代码是通过索引(get)的方法来获取相应的值,这个方法依赖 Row trait,这个是关键

处理后的数据

[
    RawRawData {
        id: 1,
        title: "111",
        subtitle: "1111",
        content: "11111",
        tag: Array [],
        create_time: 2023-02-02T03:06:17,
        normal_time: 1675278415,
        vip_pay: 10.20,
        normal_pay: 10.0,
    },
    RawRawData {
        id: 2,
        title: "2222",
        subtitle: "222",
        content: "2222",
        tag: Array [
            String("111"),
            String("222"),
        ],
        create_time: 2023-02-02T03:06:17,
        normal_time: 1675278415,
        vip_pay: 10.20,
        normal_pay: 10.0,
    },
]

原始数据

MySqlRow { row: Row { storage: b"\0\0\x01\0\0\0\x03111\x041111\x0511111\x02[]\x07\xe7\x07\x02\x02\x03\x06\x11O\xb8\xdac\x0510.20\0\0 A", values: [Some(2..6), Some(7..10), Some(11..15), Some(16..21), Some(22..24), Some(24..32), Some(32..36), Some(37..42), Some(42..46)] }, format: Binary, columns: [MySqlColumn { ordinal: 0, name: id, type_info: MySqlTypeInfo { type: Long, flags: NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT, char_set: 63, max_size: Some(11) }, flags: Some(NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT) }, MySqlColumn { ordinal: 1, name: title, type_info: MySqlTypeInfo { type: String, flags: (empty), char_set: 224, max_size: Some(80) }, flags: Some((empty)) }, MySqlColumn { ordinal: 2, name: subtitle, type_info: MySqlTypeInfo { type: VarString, flags: (empty), char_set: 224, max_size: Some(1020) }, flags: Some((empty)) }, MySqlColumn { ordinal: 3, name: content, type_info: MySqlTypeInfo { type: Blob, flags: BLOB, char_set: 224, max_size: Some(262140) }, flags: Some(BLOB) }, MySqlColumn { ordinal: 4, name: tag, type_info: MySqlTypeInfo { type: Json, flags: BLOB | BINARY, char_set: 63, max_size: Some(4294967295) }, flags: Some(BLOB | BINARY) }, MySqlColumn { ordinal: 5, name: create_time, type_info: MySqlTypeInfo { type: Datetime, flags: BINARY, char_set: 63, max_size: Some(19) }, flags: Some(BINARY) }, MySqlColumn { ordinal: 6, name: normal_time, type_info: MySqlTypeInfo { type: Long, flags: (empty), char_set: 63, max_size: Some(11) }, flags: Some((empty)) }, MySqlColumn { ordinal: 7, name: vip_pay, type_info: MySqlTypeInfo { type: NewDecimal, flags: (empty), char_set: 63, max_size: Some(12) }, flags: Some((empty)) }, MySqlColumn { ordinal: 8, name: normal, type_info: MySqlTypeInfo { type: Float, flags: (empty), char_set: 63, max_size: Some(10) }, flags: Some((empty)) }], column_names: {title: 1, id: 0, content: 3, vip_pay: 7, create_time: 5, subtitle: 2, tag: 4, normal_time: 6, normal: 8} }]

方法2,通过结构体自动转化

索引这个方法,在字段少的情况下还行,多的时间,代码量多,还要 for 二次处理,不是很方便。在已定义的结构中 RawRawData 所要的字段及对应的类型都有了,我们只要为它加一个 sqlx::FromRow 特性就可再配合 query_as 方法就可以实现自动转化,达到想要的效果。

代码量减少的同时还更加符合人性化,这种使用方法相当不错,推荐使用,要注意点是查询出来的字段一定要多于结构的字段

结构变化(部分)

#[derive(Debug, sqlx::FromRow)]
struct RawRawData {
    id: i32, 
    title: String,
    subtitle: String,
    content: String,
    tag: Value,
    create_time: NaiveDateTime,
    normal_time: i32,
    vip_pay: Decimal,
    normal_pay: f32,
}

查询部分

let data = sqlx::query_as::<_, RawRawData>("select * from test").fetch_all(&pool).await?;
println!("{:?}", data);

结果

[
    RawRawData {
        id: 1,
        title: "111",
        subtitle: "1111",
        content: "11111",
        tag: Array [],
        create_time: 2023-02-02T03:06:17,
        normal_time: 1675278415,
        vip_pay: 10.20,
        normal_pay: 10.0,
    },
    RawRawData {
        id: 2,
        title: "2222",
        subtitle: "222",
        content: "2222",
        tag: Array [
            String("111"),
            String("222"),
        ],
        create_time: 2023-02-02T03:06:17,
        normal_time: 1675278415,
        vip_pay: 10.20,
        normal_pay: 10.0,
    },
]

方法3:使用宏方法

宏方法查询来的结果不需要由手动转化,是一种比较接近 php 关联查询来的结果。可以通过字段名直接访问数据,要注意的一点是,它有很多数据类型的值都是 Option 类型(我这边除了主键是数字,其它都是 Option),主要有两个方法 query! 及 query_as!

部分代码

let rows = sqlx::query!("select * from test")
        .fetch_all(&pool).await?;
println!("{:#?}", rows);

结果

[
    Record {
        id: 1,
        title: Some(
            "111",
        ),
        subtitle: Some(
            "1111",
        ),
        content: Some(
            "11111",
        ),
        tag: Some(
            Array [],
        ),
        create_time: Some(
            2023-02-02T03:06:17,
        ),
        normal_time: Some(
            1675278415,
        ),
        vip_pay: Some(
            10.20,
        ),
        normal_pay: Some(
            10.0,
        ),
    },
    Record {
        id: 2,
        title: Some(
            "2222",
        ),
        ),
        create_time: Some(
            2023-02-02T03:06:17,
        ),
        normal_time: Some(
            1675278415,
        ),
        vip_pay: Some(
            10.20,
        ),
        normal_pay: Some(
            10.0,
        ),
    },
]

关于 execute

上面说的都是 query 也就是增删改查中的查。查关注的点是数据,增删改关注的点是影响数量,使用的方法很简单,只返回影响的数量及最新插入的id

代码

// 增加
let data = sqlx::query("INSERT INTO `test`.`test`(`id`, `title`, `subtitle`, `content`, `tag`, `create_time`, `normal_time`, `vip_pay`, `normal_pay`) VALUES (4, '2222', '222', '2222', '[\"111\", \"222\"]', '2023-02-02 03:06:17', 1675278415, 10.20, 10);
").execute(&pool).await?;
println!("{:#?}", data);

// 删除
let data = sqlx::query("DELETE FROM `test` where id = ?")
.bind(4)
.execute(&pool).await?;
println!("{:#?}", data);

对应的结果

MySqlQueryResult {
    rows_affected: 1,
    last_insert_id: 5,
}

MySqlQueryResult {
    rows_affected: 1,
    last_insert_id: 0,
}

总的来说,查询常用的方法就两个 fetch_all 及 fetch_one 上面用的都是 fetch_all ,fetch_one的用法同时。而对于增删改则只有一个execute方法。

小小问题

多数情况下sql是有条件参数的,条件一多,手动组合sql会有点麻烦,那么对于sqlx来说,怎么处理会比较好?

到此这篇关于Rust使用Sqlx连接Mysql的实现的文章就介绍到这了,更多相关Rust连接Mysql 内容请搜索程序员之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序员之家!

相关文章

  • Rust使用libloader调用动态链接库

    Rust使用libloader调用动态链接库

    这篇文章主要为大家介绍了Rust使用libloader调用动态链接库示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Rust开发WebAssembly在Html和Vue中的应用小结(推荐)

    Rust开发WebAssembly在Html和Vue中的应用小结(推荐)

    这篇文章主要介绍了Rust开发WebAssembly在Html和Vue中的应用,本文将带领大家在普通html上和vue手脚架上都来运行wasm的流程,需要的朋友可以参考下
    2022-08-08
  • Rust实现面向对象的方法

    Rust实现面向对象的方法

    这篇文章主要介绍了Rust实现面向对象的方法,Rust?并不是面向对象的语言,但是面向对象的功能都可以通过自身的特点来实现,本文通过示例代码给大家详细讲解,需要的朋友可以参考下
    2022-10-10
  • Rust实现冒泡排序算法示例详解

    Rust实现冒泡排序算法示例详解

    这篇文章主要为大家介绍了Rust实现冒泡排序算法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Rust?语言中符号?::?的使用场景解析

    Rust?语言中符号?::?的使用场景解析

    Rust?是一种强调安全性和速度的系统编程语言,这篇文章主要介绍了Rust?语言中符号?::?的使用场景,本文给大家介绍的非常详细,需要的朋友可以参考下
    2024-03-03
  • 深入探究在Rust中函数、方法和关联函数有什么区别

    深入探究在Rust中函数、方法和关联函数有什么区别

    在 Rust 中,函数、方法和关联函数都是用来封装行为的,它们之间的区别主要在于它们的定义和调用方式,本文将通过一个简单的rust代码示例来给大家讲讲Rust中函数、方法和关联函数区别,需要的朋友可以参考下
    2023-08-08
  • Rust 语言中的dyn 关键字及用途解析

    Rust 语言中的dyn 关键字及用途解析

    在Rust中,"dyn"关键字用于表示动态分发(dynamic dispatch),它通常与trait对象一起使用,以实现运行时多态, 在Rust中,多态是通过trait和impl来实现的,这篇文章主要介绍了Rust 语言中的 dyn 关键字,需要的朋友可以参考下
    2024-03-03
  • 解析Rust?struct?中的生命周期

    解析Rust?struct?中的生命周期

    rust?的生命周期保证了内存的安全性,同时也增加了开发者的心智负担。是在上线之前多费心思写代码,还是在上线以后忙忙活活查问题,这是个?trade?off?问题,这篇文章主要介绍了Rust?struct?中的生命周期,需要的朋友可以参考下
    2022-10-10
  • 从零开始使用Rust编写nginx(TLS证书快过期了)

    从零开始使用Rust编写nginx(TLS证书快过期了)

    wmproxy已用Rust实现http/https代理,?socks5代理,?反向代理,?负载均衡,?静态文件服务器,websocket代理,四层TCP/UDP转发,内网穿透等,本文给大家介绍从零开始使用Rust编写nginx(TLS证书快过期了),感兴趣的朋友一起看看吧
    2024-03-03
  • Rust语言之Copy和Clone详解

    Rust语言之Copy和Clone详解

    在 Rust 中,Copy 和 Clone trait 用于控制类型的复制行为。它们允许你定义如何复制类型的值,以及在什么情况下可以复制。本文将详细介绍这两个 trait 的作用和用法,并通过代码示例来展示它们的使用,需要的朋友可以参考下
    2023-05-05

最新评论

?


http://www.vxiaotou.com