查询
本部分是对「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 name=:name, last_update=:last_update where id=:id") | |
.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