MySQL - Node.js 语法
现代 Node.js 与 MySQL 集成
Section titled “现代 Node.js 与 MySQL 集成”Node.js 是一个强大的 JavaScript 运行时,用于构建快速且可伸缩的服务器端应用程序。一个常见的需求是将这些应用程序连接到 MySQL 数据库。本教程将指导您了解 Node.js 与 MySQL 集成的现代最佳实践,重点关注安全性、性能和可维护性。
选择合适的库:mysql 与 mysql2
Section titled “选择合适的库:mysql 与 mysql2”虽然存在多个库,但 mysql2 是现代 Node.js 应用程序的推荐选择。它是旧版 mysql 库的直接替代品 (drop-in replacement),但提供了显著优势:
- 性能:
mysql2显著更快。 - 现代 JavaScript: 完全支持
Promises和async/await,这有助于避免“回调地狱 (callback hell)”并生成更清晰的代码。 - 安全性: 对预处理语句 (Prepared Statements) 提供强大支持,以防止 SQL 注入 (SQL injection) 攻击。
- 功能: 支持压缩、SSL、流 (streaming) 等。
步骤 1:项目设置
Section titled “步骤 1:项目设置”首先,让我们设置一个新的 Node.js 项目。
# 1. 创建一个新的项目目录mkdir node-mysql-appcd node-mysql-app
# 2. 初始化一个 Node.js 项目npm init -y
# 3. 安装 mysql2 库npm install mysql2步骤 2:连接到 MySQL
Section titled “步骤 2:连接到 MySQL”对于生产应用程序,为每个查询创建新连接效率低下。最佳实践是使用连接池 (connection pool),它管理一组可重用的活动连接。
让我们创建一个名为 db.js 的文件来管理数据库连接。我们将使用基于 Promise 的 API 来编写现代异步代码。
// db.jsconst mysql = require('mysql2');
// 硬编码凭据存在安全风险。// 在实际应用中,请使用环境变量(例如,结合 `dotenv`)。const pool = mysql.createPool({ host: 'localhost', // or your db host user: 'your_user', password: 'your_password', database: 'your_database', waitForConnections: true, connectionLimit: 10, queueLimit: 0});
// 导出 Promise 封装的连接池module.exports = pool.promise();步骤 3:使用 async/await 执行查询
Section titled “步骤 3:使用 async/await 执行查询”安全优先:使用预处理语句
Section titled “安全优先:使用预处理语句”为防止 SQL 注入,您绝不应将用户输入直接拼接到 SQL 字符串中。始终使用预处理语句 (prepared statements),其中使用占位符 (?) 来表示值。mysql2 库会处理安全的替换。
// app.jsconst pool = require('./db');
async function main() { let connection; try { // 从连接池获取一个连接 connection = await pool.getConnection(); console.log('Connected to MySQL database!');
// --- 示例 1:获取数据 --- const [rows, fields] = await connection.execute('SELECT * FROM `users` WHERE `id` = ?', [1]); console.log('User found:', rows[0]);
// --- 示例 2:插入数据 --- const newUser = { name: 'Jane Doe', email: 'jane.doe@example.com' }; const [insertResult] = await connection.execute( 'INSERT INTO `users` (`name`, `email`) VALUES (?, ?)', [newUser.name, newUser.email] ); console.log('New user created with ID:', insertResult.insertId);
} catch (err) { console.error('Database error:', err); } finally { // 始终将连接释放回连接池 if (connection) connection.release(); }}
main().then(() => { // 应用程序关闭时关闭连接池 pool.end();});处理事务 (Transactions)
Section titled “处理事务 (Transactions)”对于必须全部成功或全部失败的操作(例如转账),您必须使用事务。
async function transferFunds(fromId, toId, amount) { let connection; try { connection = await pool.getConnection(); await connection.beginTransaction();
// 1. 从发送方扣款 await connection.execute('UPDATE accounts SET balance = balance - ? WHERE id = ?', [amount, fromId]);
// 2. 向接收方加款 await connection.execute('UPDATE accounts SET balance = balance + ? WHERE id = ?', [amount, toId]);
// 如果两个操作都成功,则提交事务 await connection.commit(); console.log('Transfer successful!');
} catch (err) { // 如果发生任何错误,则回滚整个事务 if (connection) await connection.rollback(); console.error('Transaction failed:', err); throw err; // 重新抛出错误以便调用方处理 } finally { if (connection) connection.release(); }}常见错误和调试
Section titled “常见错误和调试”ECONNREFUSED: 数据库服务器未运行或无法通过给定的host和port访问。ER_ACCESS_DENIED_ERROR:user或password不正确。ER_NO_SUCH_TABLE: 您查询的表在指定的database中不存在。Connection is not defined: 确保仅在成功获取连接后才释放它。finally块对此至关重要。