Rust使用Sqlx連接Mysql的實(shí)現(xiàn)
數(shù)據(jù)庫(kù)在編程中是一個(gè)很重要的環(huán)節(jié),這里是記錄rust
如何操作數(shù)據(jù)庫(kù)并以mysql
為主的做簡(jiǎn)單的使用說(shuō)明。rust
中對(duì)mysql
數(shù)據(jù)庫(kù)存有支持的我所知道的crate
:
mysql
單一驅(qū)動(dòng)sqlx
多驅(qū)動(dòng),異步,不是ORMdiesel
多驅(qū)動(dòng),異常,ORM
因?yàn)?code>mysql的crate
有點(diǎn)單一,這里主要是說(shuō)明sqlx
及diesel
的使用,分兩篇記錄。本都吃是以sqlx
為說(shuō)明
在這過(guò)程的走了不少不能言語(yǔ)的彎路主要有以下兩點(diǎn):
- 文檔都是英文,多數(shù)都是偏向于
postgres
(英文不好) select
出來(lái)的數(shù)據(jù)不直觀。不像php
那樣直接一個(gè)關(guān)聯(lián)數(shù)據(jù)解決所有問(wèn)題
sqlx
的sql是原始的,我所知道的是它不像ORM那樣,能通過(guò)一系列的方法組合成相應(yīng)的sql,sql都是手寫(xiě)的。是不是用ORM,看自己的需求進(jìn)行選擇
crate 依賴(lài)(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"
這里我使用的運(yùn)行時(shí)是tokio
,它好像支持三種運(yùn)行時(shí),另外兩種是Async-std
(runtime-async-std-native-tls),Actix-web
(runtime-actix-native-tls),不同的運(yùn)行里相應(yīng)的要在sqlx
開(kāi)啟相應(yīng)的特性支持。在這里有一個(gè)很重要的特性macros
,如果大量使用宏也就是query*!
,而且也非常方面查詢,可以讓我獲取類(lèi)似php
中操作數(shù)據(jù)庫(kù)的感覺(jué),所以這個(gè)特性很重要。
方式1:原始操作方面
通過(guò)下標(biāo)來(lái)獲取數(shù)據(jù)
.env
文件內(nèi)容
DATABASE_URL=mysql://root:root@localhost:3306/test
main.rs
文件內(nèi)容
use chrono::{NaiveDateTime}; // 處理 數(shù)據(jù)庫(kù)中 datetime 類(lèi)型 use sqlx::Row; // get 方法的 trait use sqlx::mysql::{MySqlRow, MySqlPoolOptions}; use dotenvy::dotenv; use std::{env}; use serde::{Deserialize, Serialize}; use serde_json::Value; // 處理 數(shù)據(jù)庫(kù)中 json 類(lèi)型 use rust_decimal::Decimal; // 處理 數(shù)據(jù)庫(kù)中 decimal 類(lèi)型 #[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("沒(méi)有設(shè)置數(shù)據(jù)信息"); 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(()) }
單純的查詢出來(lái)的 rows
數(shù)據(jù)打印出來(lái)有點(diǎn)初一看看不明白,沒(méi)有直觀性的 key=>value
的形式,感覺(jué)是因?yàn)閺?qiáng)類(lèi)的原因,不能單純的當(dāng)做 String=>String
來(lái)處理,所以數(shù)據(jù)庫(kù)中的類(lèi)型也要與rust
中的類(lèi)型相一一對(duì)應(yīng)。上面的代碼是通過(guò)索引(get
)的方法來(lái)獲取相應(yīng)的值,這個(gè)方法依賴(lài) Row
trait,這個(gè)是關(guān)鍵
處理后的數(shù)據(jù)
[ 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, }, ]
原始數(shù)據(jù)
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,通過(guò)結(jié)構(gòu)體自動(dòng)轉(zhuǎn)化
索引這個(gè)方法,在字段少的情況下還行,多的時(shí)間,代碼量多,還要 for
二次處理,不是很方便。在已定義的結(jié)構(gòu)中 RawRawData
所要的字段及對(duì)應(yīng)的類(lèi)型都有了,我們只要為它加一個(gè) sqlx::FromRow
特性就可再配合 query_as
方法就可以實(shí)現(xiàn)自動(dòng)轉(zhuǎn)化,達(dá)到想要的效果。
代碼量減少的同時(shí)還更加符合人性化,這種使用方法相當(dāng)不錯(cuò),推薦使用,要注意點(diǎn)是查詢出來(lái)的字段一定要多于結(jié)構(gòu)的字段
結(jié)構(gòu)變化(部分)
#[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);
結(jié)果
[
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:使用宏方法
宏方法查詢來(lái)的結(jié)果不需要由手動(dòng)轉(zhuǎn)化,是一種比較接近 php
關(guān)聯(lián)查詢來(lái)的結(jié)果??梢酝ㄟ^(guò)字段名直接訪問(wèn)數(shù)據(jù),要注意的一點(diǎn)是,它有很多數(shù)據(jù)類(lèi)型的值都是 Option
類(lèi)型(我這邊除了主鍵是數(shù)字,其它都是 Option
),主要有兩個(gè)方法 query!
及 query_as!
部分代碼
let rows = sqlx::query!("select * from test") .fetch_all(&pool).await?; println!("{:#?}", rows);
結(jié)果
[
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,
),
},
]
關(guān)于 execute
上面說(shuō)的都是 query
也就是增刪改查中的查。查關(guān)注的點(diǎn)是數(shù)據(jù),增刪改關(guān)注的點(diǎn)是影響數(shù)量,使用的方法很簡(jiǎn)單,只返回影響的數(shù)量及最新插入的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);
對(duì)應(yīng)的結(jié)果
MySqlQueryResult { rows_affected: 1, last_insert_id: 5, } MySqlQueryResult { rows_affected: 1, last_insert_id: 0, }
總的來(lái)說(shuō),查詢常用的方法就兩個(gè) fetch_all
及 fetch_one
上面用的都是 fetch_all
,fetch_one
的用法同時(shí)。而對(duì)于增刪改則只有一個(gè)execute
方法。
小小問(wèn)題
多數(shù)情況下sql
是有條件參數(shù)的,條件一多,手動(dòng)組合sql會(huì)有點(diǎn)麻煩,那么對(duì)于sqlx
來(lái)說(shuō),怎么處理會(huì)比較好?
到此這篇關(guān)于Rust使用Sqlx連接Mysql的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Rust連接Mysql 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust 中的閉包之捕獲環(huán)境的匿名函數(shù)
這篇文章介紹了Rust編程語(yǔ)言中的閉包,包括閉包的定義、使用、捕獲環(huán)境中的變量、類(lèi)型推斷與注解、與函數(shù)的比較以及實(shí)際應(yīng)用,閉包具有捕獲環(huán)境、類(lèi)型推斷和高效性等特性,是Rust中一個(gè)非常強(qiáng)大的工具,感興趣的朋友一起看看吧2025-02-02rust多個(gè)mod文件引用和文件夾mod使用注意事項(xiàng)小結(jié)
在 Rust 項(xiàng)目中,可以使用 mod 關(guān)鍵字將一個(gè)文件夾或一個(gè) rs 文件作為一個(gè)模塊引入到當(dāng)前文件中,本文給大家介紹rust多個(gè)mod文件引用和文件夾mod使用注意事項(xiàng)小結(jié),感興趣的朋友跟隨小編一起看看吧2024-03-03Rust錯(cuò)誤處理之`foo(...)?`的用法與錯(cuò)誤類(lèi)型轉(zhuǎn)換小結(jié)
foo(...)?語(yǔ)法糖為Rust的錯(cuò)誤處理提供了極大的便利,通過(guò)結(jié)合map_err方法和From?trait的實(shí)現(xiàn),你可以輕松地處理不同類(lèi)型的錯(cuò)誤,并保持代碼的簡(jiǎn)潔性和可讀性,這篇文章主要介紹了Rust錯(cuò)誤處理:`foo(...)?`的用法與錯(cuò)誤類(lèi)型轉(zhuǎn)換,需要的朋友可以參考下2024-05-05rust語(yǔ)言基礎(chǔ)pub關(guān)鍵字及Some語(yǔ)法示例
這篇文章主要為大家介紹了rust語(yǔ)言基礎(chǔ)pub關(guān)鍵字及Some語(yǔ)法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Rust動(dòng)態(tài)調(diào)用字符串定義的Rhai函數(shù)方式
Rust中使用Rhai動(dòng)態(tài)調(diào)用字符串定義的函數(shù),通過(guò)eval_expression_with_scope實(shí)現(xiàn),但參數(shù)傳遞和函數(shù)名處理有局限性,使用FnCall功能更健壯,但更復(fù)雜,總結(jié)提供了更通用的方法,但需要處理更多錯(cuò)誤情況2025-02-02