Rust 操作 MySQL
查詢
本部分是對「Rust 入門系列」Rust 中使用 MySQL[1] 的學習與記錄
-
經常使用的時間處理庫:
chrono
-
流式查詢使用:
query_iter
-
輸出到 Vec 使用:
query
-
映射到結構體使用:
query_map
-
獲取單條數據使用:
query_first
-
命名參數查詢使用:
exec_first
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL,
`age` int(11) NOT NULL,
`id_card` varchar(128) NOT NULL,
`last_update` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 插入測試數據
insert into student (name, age, id_card, last_update) values ('張三', 23, '123456789X', CURRENT_DATE());
insert into student (name, age, id_card, last_update) values ('李四', 24, '8382353902', CURRENT_DATE())
注意,mysql[2] 這個 crate 新版本 demo 有問題,文檔的更新速度跟不上代碼的修改腳步..
需要指定版本:
[dependencies]
mysql = "20.0.0" #通配符*表示可以使用任何版本,通常會拉取最新版本;此處需要指定,不使用最新版本
流式查詢
use chrono::prelude::*;// 用來處理日期
use mysql::*;
use mysql::prelude::*;
fn main() {
let url = "mysql://root:12345678@localhost:3306/shuang";
let pool = Pool::new(url).unwrap(); // 獲取連接池
let mut conn = pool.get_conn().unwrap();// 獲取鏈接
conn.query_iter("select * from student")
.unwrap()
.for_each(|row| {
let r: (i32, String, i32, String, NaiveDate) = from_row(row.unwrap());
println!("{}, {},{},{}, {:?}", r.0, r.1, r.2, r.3, r.4);
});
}
row 的類型是mysql_common::row::Row
,其把數據以字節的形式存儲。
所以需將低級的字節轉換成想要的類型 如 i32,String,這裏使用了from_row
。注意,轉換後的數據以元組的形式返回,其中每一項和選擇列的順序相同。
輸出:
1, 張三,23,123456789X, 2022-04-26
2, 李四,24,8382353902, 2022-04-26
聚合查詢結果
其實還可以將查詢結果收集到 Vec 中。Vec 中的每個元素都是一個元組。
query 函數已經將字節轉換爲選擇的數據類型,因此不需要再轉換了。需要注意的是,這裏必須明確元組的數據類型 (如此處是 Vec<(i32, String, i32, String, NaiveDate)>
)。否則,編譯器沒辦法做轉換。
use chrono::prelude::*;// 用來處理日期
use mysql::*;
use mysql::prelude::*;
fn main() {
let url = "mysql://root:12345678@localhost:3306/shuang";
let pool = Pool::new(url).unwrap(); // 獲取連接池
let mut conn = pool.get_conn().unwrap();// 獲取鏈接
// 輸出到Vec
let res: Vec<(i32, String, i32, String, NaiveDate)> =
conn.query("select * from student").unwrap();
for r in res {
println!("{}, {},{},{}, {:?}", r.0, r.1, r.2, r.3, r.4);
}
}
映射結果到結構體
如果表的列數很多,使用元組容易混淆,更普遍的做法是定義一個結構體。
如下定義一個 Student 結構體, 然後可以用query_map
將查詢結果映射到 Student 中。
不需要指定數據類型,編譯器會根據 Student 類型自動推導
use chrono::prelude::*;// 用來處理日期
use mysql::*;
use mysql::prelude::*;
fn main() {
let url = "mysql://root:12345678@localhost:3306/shuang";
let pool = Pool::new(url).unwrap(); // 獲取連接池
let mut conn = pool.get_conn().unwrap();// 獲取鏈接
// 將結果映射到提前定義好的結構體
struct Student {
id: u64,
name: String,
age: u16,
id_card: String,
last_changed_on: NaiveDate,
}
let res = conn.query_map(
"select * from student",
|(id, name, age, id_card, update)| Student {
id: id,
name: name,
age: age,
id_card: id_card,
last_changed_on: update,
},
).expect("Query failed.");
for i in res {
println!(
"{}, {},{},{}, {:?}",
i.id, i.name, i.age, i.id_card, i.last_changed_on
)
}
}
單條數據查詢
查詢特定數據行,可能會出現下面幾種情況:
-
找到,返回實際數據
-
沒有找到行
-
發生錯誤
所以,使用query_first
函數返回的是 Option 的結果。需要將其解包兩次纔可以獲取實際的行數據:
use chrono::prelude::*;// 用來處理日期
use mysql::*;
use mysql::prelude::*;
fn main() {
let url = "mysql://root:12345678@localhost:3306/shuang";
let pool = Pool::new(url).unwrap(); // 獲取連接池
let mut conn = pool.get_conn().unwrap();// 獲取鏈接
struct Student {
id: u64,
name: String,
age: u16,
id_card: String,
last_changed_on: NaiveDate,
}
// 條件查詢,查詢單個數據
let res = conn.query_first("select * from student where name = '張三'")
.map(
// Unpack Result
|row| {
row.map(|(id, name, age, id_card, update)| Student {
id: id,
name: name,
age: age,
id_card: id_card,
last_changed_on: update,
})
},
);
match res.unwrap() {
Some(student) => println!(
"{}, {},{},{}, {:?}",
student.id, student.name, student.age, student.id_card, student.last_changed_on
),
None => println!("Sorry no student found."),
}
}
命名參數的使用
use chrono::prelude::*;// 用來處理日期
use mysql::*;
use mysql::prelude::*;
fn main() {
let url = "mysql://root:12345678@localhost:3306/shuang";
let pool = Pool::new(url).unwrap(); // 獲取連接池
let mut conn = pool.get_conn().unwrap();// 獲取鏈接
struct Student {
id: u64,
name: String,
age: u16,
id_card: String,
last_changed_on: NaiveDate,
}
let res = conn
.exec_first(
"select * from student where name = :name",
params! {
"name" => "李四"
},
)
.map(
// Unpack Result
|row| {
row.map(|(id, name, age, id_card, update)| Student {
id: id,
name: name,
age: age,
id_card: id_card,
last_changed_on: update,
})
},
);
match res.unwrap() {
Some(student) => println!(
"{}, {},{},{}, {:?}",
student.id, student.name, student.age, student.id_card, student.last_changed_on
),
None => println!("Sorry no student found."),
}
}
寫操作
本部分是對 Rust 使用 MySQL 數據庫 02[3] 的學習與記錄
-
插入數據使用
conn.exec_drop()
-
使用預編譯語句插入大量數據,
conn.prep()
-
使用
conn.last_insert_id()
可以獲取主鍵 -
更新和刪除也使用
conn.prep
和conn.exec_drop
插入新數據
use chrono::prelude::*;
// 用來處理日期
use mysql::*;
use mysql::prelude::*;
fn main() {
let url = "mysql://root:12345678@localhost:3306/shuang";
let pool = Pool::new(url).unwrap(); // 獲取連接池
let mut conn = pool.get_conn().unwrap();// 獲取鏈接
conn.exec_drop(
"INSERT INTO student (name, age, id_card, last_update) VALUES (:name, :age, :id_card, :last_update)",
params! {
"name" => "王五",
"age" => 28,
"id_card" => "66666688",
"last_update" => today(),
}).unwrap();
}
fn today() -> NaiveDate {
let l = Local::today();
NaiveDate::from_ymd(l.year(), l.month(), l.day())
}
和上面一節一樣,命名參數在這裏使用了 params 宏的語法
exec_drop
方法中的 drop 表示沒有返回結果
用於執行插入 / 更新 / 刪除的 sql
使用預編譯語句
使用conn.prep
將 sql 編譯成預編譯語句。
use chrono::prelude::*;
// 用來處理日期
use mysql::*;
use mysql::prelude::*;
fn main() {
let url = "mysql://root:12345678@localhost:3306/shuang";
let pool = Pool::new(url).unwrap(); // 獲取連接池
let mut conn = pool.get_conn().unwrap();// 獲取鏈接
let stmt = conn.prep("INSERT INTO student (name, age, id_card, last_update) VALUES (:name, :age, :id_card, :last_update)")
.unwrap();
for i in 1..10 {
conn.exec_drop(&stmt, params! {
"name" => "dashen",
"age" => 18 + i,
"id_card" => "1234565X",
"last_update" => NaiveDate::from_ymd(2017, 05, 04),
}).unwrap()
}
}
獲取生成的主鍵 id
可以通過conn.last_insert_id()
方法獲取到新記錄的主鍵 id,該方法將返回的一個類型爲 u64 的值
use chrono::prelude::*;
// 用來處理日期
use mysql::*;
use mysql::prelude::*;
fn main() {
let url = "mysql://root:12345678@localhost:3306/shuang";
let pool = Pool::new(url).unwrap(); // 獲取連接池
let mut conn = pool.get_conn().unwrap();// 獲取鏈接
conn.exec_drop("INSERT INTO student (name, age, id_card, last_update) VALUES (:name, :age, :id_card, :last_update)", params! {
"name" => "fliter",
"age" => 29,
"id_card" => "88888888",
"last_update" => NaiveDate::from_ymd(2022, 05, 04),
}).unwrap();
println!("新插入的記錄的主鍵爲: {}", conn.last_insert_id())
}
新插入的記錄的主鍵爲: 13
更新和刪除
類似於插入操作
use chrono::prelude::*;
// 用來處理日期
use mysql::*;
use mysql::prelude::*;
fn main() {
let url = "mysql://root:12345678@localhost:3306/shuang";
let pool = Pool::new(url).unwrap(); // 獲取連接池
let mut conn = pool.get_conn().unwrap();// 獲取鏈接
let stmt = conn.prep("update student set )
.unwrap();
conn.exec_drop(&stmt, params! {
"name" => "新名字",
"last_update" => NaiveDate::from_ymd(2038, 12, 31),
"id" => 10,
}).unwrap();
let stmt = conn.prep("delete from student where id=:id").unwrap();
conn.exec_drop(&stmt, params! {
"id" => 12,
}).unwrap();
}
參考資料
[1]
「Rust 入門系列」Rust 中使用 MySQL: https://rustmagazine.github.io/rust_magazine_2021/chapter_3/rust-mysql.html
[2]
mysql: https://crates.io/crates/mysql
[3]
Rust 使用 MySQL 數據庫 02: https://www.modb.pro/db/179746
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/VRlydU9-zTMNHja6mrIySg