Node.js 连接 MySQL
以下是关于如何使用 Node.js 连接 MySQL 数据库的中文讲解,内容涵盖环境准备、安装依赖、连接数据库、执行基本 CRUD 操作以及注意事项,帮助你快速上手 Node.js 与 MySQL 的结合开发。
1. 为什么使用 Node.js 连接 MySQL?
Node.js 是一个高效的 JavaScript 运行时,适合构建高并发、I/O 密集型应用,而 MySQL 是一种广泛使用的关系型数据库,适合存储结构化数据。结合两者可以实现:
- 高效数据处理:Node.js 的异步非阻塞特性与 MySQL 的数据存储能力结合,适合 Web 应用、API 服务等场景。
- 灵活开发:通过 Node.js 操作 MySQL,可以轻松实现数据的增删改查(CRUD)操作。
- 广泛应用:许多 Web 应用(如博客、电商、后台管理系统)都依赖 Node.js 和 MySQL。
2. 环境准备
在开始之前,确保以下环境已配置好:
- Node.js:已安装 Node.js(建议版本 12 或以上)。验证:
node --version
npm --version
- MySQL:已安装 MySQL 服务器(社区版或企业版均可),并启动 MySQL 服务。验证:
mysql --version
- 数据库和表:创建一个测试数据库(如
test_db
)和表(如users
),用于后续操作。
示例 SQL:
CREATE DATABASE test_db;
USE test_db;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL
);
3. 安装 MySQL 驱动
Node.js 通过 mysql2
模块连接 MySQL,mysql2
是目前推荐的 MySQL 驱动,支持 Promise 和回调两种方式,性能优于旧的 mysql
模块。
安装 mysql2
:
在项目目录下运行:
npm install mysql2
项目结构示例:
project/
├── index.js
├── package.json
├── node_modules/
4. 连接 MySQL 数据库
以下是一个简单的示例,展示如何使用 mysql2
连接 MySQL 数据库。
示例代码(index.js
):
const mysql = require('mysql2');
// 创建数据库连接配置
const connection = mysql.createConnection({
host: 'localhost', // MySQL 服务器地址
user: 'root', // MySQL 用户名
password: 'your_password', // MySQL 密码
database: 'test_db' // 数据库名称
});
// 连接数据库
connection.connect((err) => {
if (err) {
console.error('连接失败:', err.stack);
return;
}
console.log('连接成功,连接 ID:', connection.threadId);
});
// 关闭连接(通常在程序结束时)
connection.end();
运行代码:
node index.js
输出:
连接成功,连接 ID: 12345
说明:
- 配置参数:
host
:MySQL 服务器地址,通常为localhost
(本地)或远程服务器 IP。user
:MySQL 用户名,默认为root
。password
:MySQL 密码,替换为你的实际密码。database
:要连接的数据库名称。- 错误处理:使用回调函数检查连接是否成功。
- 连接关闭:
connection.end()
用于在操作完成后关闭连接,避免资源泄漏。
5. 执行 CRUD 操作
以下展示如何使用 mysql2
进行基本的增删改查操作。
5.1 创建(Create) – 插入数据
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'your_password',
database: 'test_db'
});
// 插入数据
const sql = 'INSERT INTO users (name, email) VALUES (?, ?)';
const values = ['张三', 'zhangsan@example.com'];
connection.query(sql, values, (err, results) => {
if (err) {
console.error('插入失败:', err);
return;
}
console.log('插入成功:', results);
connection.end();
});
输出:
插入成功: ResultSetHeader {
fieldCount: 0,
affectedRows: 1,
insertId: 1,
...
}
说明:
- 使用
?
占位符防止 SQL 注入,values
数组的值会按顺序替换占位符。 results
包含插入操作的元信息,如affectedRows
(影响的行数)和insertId
(新插入记录的 ID)。
5.2 读取(Read) – 查询数据
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'your_password',
database: 'test_db'
});
// 查询数据
const sql = 'SELECT * FROM users';
connection.query(sql, (err, results) => {
if (err) {
console.error('查询失败:', err);
return;
}
console.log('查询结果:', results);
connection.end();
});
输出:
查询结果: [
{ id: 1, name: '张三', email: 'zhangsan@example.com' }
]
说明:
results
是一个数组,包含查询到的所有记录。- 如果需要特定条件查询,可使用
WHERE
,如SELECT * FROM users WHERE id = ?
。
5.3 更新(Update) – 修改数据
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'your_password',
database: 'test_db'
});
// 更新数据
const sql = 'UPDATE users SET email = ? WHERE name = ?';
const values = ['zhangsan_new@example.com', '张三'];
connection.query(sql, values, (err, results) => {
if (err) {
console.error('更新失败:', err);
return;
}
console.log('更新成功:', results.affectedRows, '条记录被更新');
connection.end();
});
输出:
更新成功: 1 条记录被更新
5.4 删除(Delete) – 删除数据
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'your_password',
database: 'test_db'
});
// 删除数据
const sql = 'DELETE FROM users WHERE id = ?';
const values = [1];
connection.query(sql, values, (err, results) => {
if (err) {
console.error('删除失败:', err);
return;
}
console.log('删除成功:', results.affectedRows, '条记录被删除');
connection.end();
});
输出:
删除成功: 1 条记录被删除
6. 使用 Promise 方式(推荐)
mysql2
支持 Promise API,使用 async/await
语法可以让代码更简洁、更易读。
示例:使用 Promise 进行 CRUD
const mysql = require('mysql2/promise'); // 使用 mysql2/promise
async function main() {
// 创建连接
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'your_password',
database: 'test_db'
});
try {
// 插入
await connection.execute('INSERT INTO users (name, email) VALUES (?, ?)', ['李四', 'lisi@example.com']);
console.log('插入成功');
// 查询
const [rows] = await connection.execute('SELECT * FROM users');
console.log('查询结果:', rows);
// 更新
await connection.execute('UPDATE users SET email = ? WHERE name = ?', ['lisi_new@example.com', '李四']);
console.log('更新成功');
// 删除
await connection.execute('DELETE FROM users WHERE name = ?', ['李四']);
console.log('删除成功');
} catch (err) {
console.error('操作失败:', err);
} finally {
// 关闭连接
await connection.end();
}
}
main();
输出:
插入成功
查询结果: [ { id: 2, name: '李四', email: 'lisi@example.com' } ]
更新成功
删除成功
说明:
- 使用
mysql2/promise
导入 Promise 版本的mysql2
。 connection.execute
比connection.query
更安全,返回[rows, fields]
格式。- 使用
try/catch
捕获异步错误。 - 确保在
finally
中关闭连接。
7. 使用连接池(生产环境推荐)
在高并发场景下,频繁创建和关闭连接会影响性能。使用连接池可以复用数据库连接,提高效率。
示例:使用连接池
const mysql = require('mysql2/promise');
// 创建连接池
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'your_password',
database: 'test_db',
waitForConnections: true, // 是否等待空闲连接
connectionLimit: 10, // 最大连接数
queueLimit: 0 // 排队请求数(0 表示无限制)
});
async function main() {
try {
// 从连接池获取连接
const connection = await pool.getConnection();
// 查询
const [rows] = await connection.execute('SELECT * FROM users');
console.log('查询结果:', rows);
// 释放连接回连接池
connection.release();
} catch (err) {
console.error('操作失败:', err);
} finally {
// 关闭连接池
await pool.end();
}
}
main();
说明:
createPool
创建一个连接池,管理多个连接。getConnection
从池中获取一个连接,使用后通过release
归还。connectionLimit
控制最大连接数,根据服务器性能调整。- 适合高并发场景,如 Web 服务器。
8. 注意事项
- 防止 SQL 注入:
- 始终使用
?
占位符或参数化查询(如connection.execute
),避免直接拼接 SQL 字符串。 - 错误示例:
javascript const sql = `SELECT * FROM users WHERE name = '${name}'`; // 不安全,易受 SQL 注入攻击
- 错误处理:
- 检查连接错误、查询错误(如表不存在、权限不足)。
- 使用
try/catch
或回调函数中的err
参数处理错误。
- 连接管理:
- 单次操作后关闭连接(
connection.end()
)。 - 高并发场景使用连接池(
createPool
)。
- 性能优化:
- 索引数据库表以提高查询性能。
- 限制查询返回的行数(如
LIMIT 10
)。 - 使用连接池避免频繁建立连接。
- 安全性:
- 不要在代码中硬编码数据库密码,建议使用环境变量(如
process.env.DB_PASSWORD
)。 - 示例(使用
dotenv
):bash npm install dotenv
.env
文件:DB_HOST=localhost DB_USER=root DB_PASSWORD=your_password DB_NAME=test_db
代码:javascript require('dotenv').config(); const connection = mysql.createConnection({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME });
- MySQL 配置:
- 确保 MySQL 服务正在运行。
- 检查用户权限(如
root
是否有访问test_db
的权限)。 - 远程连接需配置 MySQL 允许远程访问(修改
bind-address
或授予用户权限)。
9. 实际案例:Express + MySQL
以下是一个结合 Express 和 MySQL 的简单 REST API 示例。
示例代码(app.js
):
const express = require('express');
const mysql = require('mysql2/promise');
const app = express();
app.use(express.json());
// 创建连接池
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'your_password',
database: 'test_db',
connectionLimit: 10
});
// 获取所有用户
app.get('/users', async (req, res) => {
try {
const [rows] = await pool.execute('SELECT * FROM users');
res.json(rows);
} catch (err) {
res.status(500).json({ error: '查询失败' });
}
});
// 添加用户
app.post('/users', async (req, res) => {
const { name, email } = req.body;
try {
await pool.execute('INSERT INTO users (name, email) VALUES (?, ?)', [name, email]);
res.status(201).json({ message: '用户添加成功' });
} catch (err) {
res.status(500).json({ error: '添加失败' });
}
});
app.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
测试 API:
- 运行:
node app.js
- 使用
curl
或 Postman 测试:
- 获取用户:
curl http://localhost:3000/users
- 添加用户:
curl -X POST -H "Content-Type: application/json" -d '{"name":"王五","email":"wangwu@example.com"}' http://localhost:3000/users
10. 总结
- 模块选择:使用
mysql2
模块,支持回调和 Promise,推荐 Promise 方式以简化异步代码。 - 连接方式:单次连接适合简单脚本,连接池适合高并发场景。
- 安全与性能:使用参数化查询防止 SQL 注入,优化数据库索引和连接池配置。
- 实际应用:结合 Express 等框架,轻松构建 REST API 或 Web 应用。
如果你需要更复杂的示例(如事务处理、分页查询)或有其他问题,请随时告诉我!