第七部分:第二节 - 在 Node.js 中连接和操作 MySQL:厨房与仓库的沟通渠道
要让 Node.js 后端应用(厨房)与 MySQL 数据库(仓库)进行交互,我们需要一个数据库驱动程序 (Database Driver)。驱动程序就像厨房与仓库之间的通信员,它知道如何用仓库管理员(MySQL Server)能理解的语言(SQL)发送指令,并把仓库的回应(查询结果)带回来。
Node.js 有多个可用的 MySQL 驱动,比较流行且推荐的是 mysql2
,它支持 Promise API,更适合现代异步编程风格。
安装 mysql2
:
在你的 Node.js 项目目录中(非 NestJS 项目,先以纯 Node.js 脚本为例):
npm install mysql2
# 或者 yarn add mysql2
建立数据库连接:
在操作数据库之前,首先需要建立一个连接。连接信息包括数据库地址(host)、端口、用户名、密码、数据库名等。
// connect_db.js
const mysql = require('mysql2');// 创建数据库连接
const connection = mysql.createConnection({host: 'localhost', // 数据库地址user: 'your_mysql_username', // 你的 MySQL 用户名password: 'your_mysql_password', // 你的 MySQL 密码database: 'my_webapp_db' // 要连接的数据库名
});// 连接到数据库
connection.connect(err => {if (err) {console.error('数据库连接失败:', err.stack);return;}console.log('成功连接到数据库,连接 ID:', connection.threadId);// 在这里执行数据库操作...// 操作完成后关闭连接 (在实际应用中通常使用连接池)// connection.end();
});// 注意:在实际应用中,不应将敏感信息直接写在代码中,应使用配置文件或环境变量。
执行 SQL 查询:
mysql2
支持回调和 Promise 两种方式执行查询。推荐使用 Promise 方式,因为它与 async/await
配合更优雅。
Promise 方式:
// query_db.js
const mysql = require('mysql2/promise'); // 导入 Promise 版本的驱动async function runQueries() {let connection;try {// 建立连接connection = await mysql.createConnection({host: 'localhost',user: 'your_mysql_username',password: 'your_mysql_password',database: 'my_webapp_db'});console.log('成功连接到数据库');// 执行 SELECT 查询const [rows, fields] = await connection.execute('SELECT * FROM users');console.log('查询结果:', rows); // rows 是一个包含查询结果的数组// 执行带参数的查询 (使用占位符 ?)const userId = 1;const [userRows] = await connection.execute('SELECT * FROM users WHERE id = ?', [userId]);console.log(`查询用户 ID ${userId}:`, userRows[0]); // 获取第一行结果// 执行 INSERT 查询const newUser = { username: 'charlie', email: 'charlie@example.com' };const [insertResult] = await connection.execute('INSERT INTO users (username, email) VALUES (?, ?)', [newUser.username, newUser.email]);console.log('插入结果:', insertResult);console.log('新用户 ID:', insertResult.insertId); // 获取新插入记录的 ID} catch (err) {console.error('数据库操作出错:', err);} finally {// 确保连接关闭if (connection) {await connection.end();console.log('数据库连接已关闭');}}
}runQueries();// 运行这个文件: node query_db.js
connection.execute()
方法返回一个 Promise, resolved 后得到一个数组,第一个元素是查询结果(对于 SELECT 是行数据,对于 INSERT/UPDATE/DELETE 是操作影响的信息),第二个元素是字段信息(通常不常用)。
连接池 (Connection Pool):
反复创建和关闭数据库连接是有开销的。在高并发场景下,为每个请求都创建新连接会导致性能问题。连接池是一组预先创建好的数据库连接,当需要执行数据库操作时,从池中获取一个连接;操作完成后,将连接归还给池,而不是关闭。这就像仓库门口有一个服务台,提前准备好了一些推车(连接),需要搬东西时直接领用推车,用完归还,而不是每次都去制造一个新推车。
在 Node.js 应用中,尤其是在 Express 或 NestJS 应用中,强烈推荐使用连接池。
// pool_db.js
const mysql = require('mysql2/promise');// 创建连接池
const pool = mysql.createPool({host: 'localhost',user: 'your_mysql_username',password: 'your_mysql_password',database: 'my_webapp_db',waitForConnections: true, // 如果连接都忙,等待连接可用connectionLimit: 10, // 池中最大连接数queueLimit: 0 // 连接队列最大长度,0 表示无限制
});async function getUserById(userId) {let connection;try {// 从连接池获取一个连接connection = await pool.getConnection();console.log(`连接 ID ${connection.threadId} 从连接池获取`);const [rows] = await connection.execute('SELECT * FROM users WHERE id = ?', [userId]);return rows[0]; // 返回第一个用户} catch (err) {console.error('获取用户出错:', err);throw err; // 抛出错误以便上层处理} finally {// 确保连接归还给连接池if (connection) {connection.release();console.log(`连接 ID ${connection.threadId} 归还给连接池`);}}
}// 调用函数获取用户
async function main() {const user = await getUserById(2);console.log('查找到的用户:', user);// 当应用关闭时,关闭连接池 (在 Express/NestJS 应用退出时执行)// await pool.end();// console.log('连接池已关闭');
}main();// 运行这个文件: node pool_db.js
小结: 使用数据库驱动程序(如 mysql2
)是 Node.js 连接和操作 MySQL 的方式。Promise API 结合 async/await
使得异步数据库操作更易读。连接池是管理数据库连接、提高应用性能的关键,尤其在处理并发请求时。
练习:
- 在你之前的 Node.js 项目(非 Express/NestJS)中安装
mysql2
。 - 使用 Promise 方式编写一个脚本,连接到你的
my_bookstore_db
数据库。 - 执行一个
SELECT * FROM books
查询,并将所有书籍信息打印到控制台。 - 编写一个函数,接收书籍 ID 和新的出版年份作为参数,使用
UPDATE
语句更新指定书籍的出版年份。在脚本中调用该函数并测试。 - (进阶)修改你的脚本,使用连接池来执行查询。