要让 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
语句更新指定书籍的出版年份。在脚本中调用该函数并测试。 - (进阶)修改你的脚本,使用连接池来执行查询。