数据库:MySQL
SQLite 只是为了效果演示。如果你需要一个生产环境可用的数据库,可尝试 MySQL 或 mongoDB。
切换到 MySQL
- 在你的机器上安装 MySQL 并启动。
注意:在实际生产项目中,记得给表中字段创建所需的索引。
- 添加 mysql 依赖:
cargo add mysql
Cargo.toml 中的自动更新:
@@ -8,6 +8,7 @@ edition = "2021"
[dependencies]
chrono = { version = "0.4.35", features = ["serde"] }
lazy_static = "1.4.0"
+mysql = "24.0.0"
rocket = { version = "0.5.0", features = ["json"] }
rusqlite = "0.31.0"
serde = { version = "1.0.197", features = ["derive"] }
- 更新代码。
添加 infrastructure/database/mysql.rs:
use std::error::Error;
use chrono::Utc;
use mysql::prelude::Queryable;
use mysql::{Error as MySQLError, Pool};
use crate::domain::gateway::BookManager;
use crate::domain::model;
pub struct MySQLPersistence {
pool: Pool,
}
impl MySQLPersistence {
pub fn new(dsn: &str) -> Result<Self, MySQLError> {
let pool = Pool::new(dsn)?;
Ok(MySQLPersistence { pool })
}
}
impl BookManager for MySQLPersistence {
fn create_book(&self, b: &model::Book) -> Result<u32, Box<dyn Error>> {
let mut conn = self.pool.get_conn()?;
conn.exec::<usize, &str, (String, String, String, String, String, u32)>(
"INSERT INTO books (title, author, published_at, description, isbn, total_pages)
VALUES (?, ?, ?, ?, ?, ?)",
(
b.title.clone(),
b.author.clone(),
b.published_at.clone(),
b.description.clone(),
b.isbn.clone(),
b.total_pages,
),
)?;
Ok(conn.last_insert_id() as u32)
}
fn update_book(&self, id: u32, b: &model::Book) -> Result<(), Box<dyn Error>> {
let mut conn = self.pool.get_conn()?;
conn.exec::<usize, &str, (String, String, String, String, String, u32, String, u32)>(
"UPDATE books SET title = ?, author = ?, published_at = ?, description = ?, isbn = ?, total_pages = ?, updated_at = ?
WHERE id = ?",
(b.title.clone(), b.author.clone(), b.published_at.clone(), b.description.clone(), b.isbn.clone(), b.total_pages, Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(), id),
)?;
Ok(())
}
fn delete_book(&self, id: u32) -> Result<(), Box<dyn Error>> {
let mut conn = self.pool.get_conn()?;
conn.exec::<usize, &str, (u32,)>("DELETE FROM books WHERE id = ?", (id,))?;
Ok(())
}
fn get_book(&self, id: u32) -> Result<Option<model::Book>, Box<dyn Error>> {
let mut conn = self.pool.get_conn()?;
let books = conn.query_map(
format!("SELECT * FROM books WHERE ID = {}", id),
|(
id,
title,
author,
published_at,
description,
isbn,
total_pages,
created_at,
updated_at,
): (
u64,
String,
String,
String,
String,
String,
u64,
String,
String,
)| {
model::Book {
id: id as u32,
title,
author,
published_at,
description,
isbn,
total_pages: total_pages as u32,
created_at,
updated_at,
}
},
)?;
Ok(books.first().cloned())
}
fn get_books(&self) -> Result<Vec<model::Book>, Box<dyn Error>> {
let mut conn = self.pool.get_conn()?;
let books = conn.query_map(
"SELECT * FROM books",
|(
id,
title,
author,
published_at,
description,
isbn,
total_pages,
created_at,
updated_at,
): (
u64,
String,
String,
String,
String,
String,
u64,
String,
String,
)| {
model::Book {
id: id as u32,
title,
author,
published_at,
description,
isbn,
total_pages: total_pages as u32,
created_at,
updated_at,
}
},
)?;
Ok(books)
}
}
DSN 表示数据源名称(Data Source Name)。它是用于向应用程序提供数据实例连接信息的字符串。
调整 infrastructure/config/mod.rs 以导出符号:
@@ -1,3 +1,3 @@
-mod sqlite;
+mod mysql;
-pub use sqlite::SQLitePersistence;
+pub use mysql::MySQLPersistence;
向 DBConfig
结构体添加 dsn
配置项,infrastructure/config/mod.rs:
@@ -9,6 +9,7 @@ pub struct Config {
#[derive(Debug, Deserialize, Serialize)]
pub struct DBConfig {
pub file_name: String,
+ pub dsn: String,
}
#[derive(Debug, Deserialize, Serialize)]
更新 WireHelper
以切换依赖,application/wire_helper.rs:
@@ -5,12 +5,12 @@ use crate::infrastructure::database;
use crate::infrastructure::Config;
pub struct WireHelper {
- persistence: Arc<database::SQLitePersistence>,
+ persistence: Arc<database::MySQLPersistence>,
}
impl WireHelper {
pub fn new(c: &Config) -> Result<Self, Box<dyn std::error::Error>> {
- let persistence = Arc::new(database::SQLitePersistence::new(&c.db.file_name)?);
+ let persistence = Arc::new(database::MySQLPersistence::new(&c.db.dsn)?);
Ok(WireHelper { persistence })
}
在 main.rs 中放入 dsn
值:
@@ -14,6 +14,7 @@ fn rocket() -> _ {
app: ApplicationConfig { port: 8000 },
db: DBConfig {
file_name: "test.db".to_string(),
+ dsn: "mysql://test_user:test_pass@127.0.0.1:3306/lr_book".to_string(),
},
};
let wire_helper = application::WireHelper::new(&c).expect("Failed to create WireHelper");
瞧!你的 API 服务器现在由 MySQL 驱动啦!