» Node.js:使用Express构建REST API » 2. 开发 » 2.6 数据库:MySQL

数据库:MySQL

SQLite 只是为了效果演示。如果你需要一个生产环境可用的数据库,可尝试 MySQLmongoDB

切换到 MySQL

  1. 在你机器上安装 MySQL 并启动。

注意:在实际生产项目中,记得给表中字段创建所需的索引。

  1. 添加 mysql 依赖:
npm install mysql2
  1. 更新代码。

添加 infrastructure/database/mysql.ts

import mysql, { ResultSetHeader, RowDataPacket } from "mysql2";

import { Book } from "@/domain/model/book";
import { BookManager } from "@/domain/gateway/book_manager";

export class MySQLPersistence implements BookManager {
  private db: mysql.Connection;

  constructor(dsn: string) {
    this.db = mysql.createConnection(dsn);
    this.db.addListener("error", (err) => {
      console.error("Error connecting to MySQL:", err.message);
    });

    this.db.execute(
      `CREATE TABLE IF NOT EXISTS books (
        id INT AUTO_INCREMENT PRIMARY KEY,
        title VARCHAR(255) NOT NULL,
        author VARCHAR(255) NOT NULL,
        published_at VARCHAR(15) NOT NULL,
        description TEXT NOT NULL,
        isbn VARCHAR(255) NOT NULL,
        total_pages INT NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
        )`,
      (err) => {
        if (err) {
          console.error("Error in MySQL:", err.message);
        } else {
          console.log("Successfully initialized tables.");
        }
      }
    );
  }

  async createBook(b: Book): Promise<number> {
    const { title, author, published_at, description, isbn, total_pages } = b;
    const [result] = await this.db
      .promise()
      .query(
        "INSERT INTO books (title, author, published_at, description, isbn, total_pages) VALUES (?, ?, ?, ?, ?, ?)",
        [title, author, published_at, description, isbn, total_pages]
      );
    return (result as ResultSetHeader).insertId;
  }

  async updateBook(id: number, b: Book): Promise<void> {
    const { title, author, published_at, description, isbn, total_pages } = b;
    await this.db
      .promise()
      .query(
        "UPDATE books SET title = ?, author = ?, published_at = ?, description = ?, isbn = ?, total_pages = ? WHERE id = ?",
        [title, author, published_at, description, isbn, total_pages, id]
      );
  }

  async deleteBook(id: number): Promise<void> {
    await this.db.promise().query("DELETE FROM books WHERE id = ?", [id]);
  }

  async getBook(id: number): Promise<Book | null> {
    let [rows] = await this.db
      .promise()
      .query("SELECT * FROM books WHERE id = ?", [id]);
    rows = rows as RowDataPacket[];
    return rows.length ? (rows[0] as Book) : null;
  }

  async getBooks(): Promise<Book[]> {
    const [rows] = await this.db.promise().query("SELECT * FROM books");
    return rows as Book[];
  }

  close(): void {
    this.db.end();
  }
}

注意与之前的关键区别,此处构建数据库连接时使用来自 mysql2mysql.createConnection(dsn) 而不是来自 sqlite3new sqlite3.Database(dbFilePath, ...)

DSN 表示数据源名称(Data Source Name)。它是用于向应用程序提供数据实例连接信息的字符串。

DBConfig 接口添加 dsn 配置项,在 infrastructure/config/config.ts 中:

@@ -1,5 +1,6 @@
 interface DBConfig {
   fileName: string;
+  dsn: string;
 }

更新 WireHelper 以切换依赖,在 application/wire_helper.ts 中:

@@ -1,13 +1,13 @@
-import { SQLitePersistence } from "@/infrastructure/database";
+import { MySQLPersistence } from "@/infrastructure/database";
 import { Config } from "@/infrastructure/config";
 import { BookManager } from "@/domain/gateway";
 
 // WireHelper is the helper for dependency injection
 export class WireHelper {
-  private persistence: SQLitePersistence;
+  private persistence: MySQLPersistence;
 
   constructor(c: Config) {
-    this.persistence = new SQLitePersistence(c.db.fileName);
+    this.persistence = new MySQLPersistence(c.db.dsn);
   }
 
   bookManager(): BookManager {

main.ts 中放入 dsn 值:

@@ -9,6 +9,7 @@ const c = {
   },
   db: {
     fileName: "test.db",
+    dsn: "mysql://test_user:test_pass@127.0.0.1:3306/lr_book?charset=utf8mb4",
   },
 };
 const wireHelper = new WireHelper(c);

瞧!你的 API 服务器现在由 MySQL 驱动啦!