01. Node.js 连接数据库的基础
Node.js 操作数据库跟其他语言原理一样,但因为是异步的,所有数据库操作都是非阻塞的——用回调、Promise 或 async/await。
三种常见模式:
1. 原生驱动——mysql2、pg (PostgreSQL)、mongodb(MongoDB 官方驱动)。直接发 SQL 或命令,最底层最灵活。
2. 查询构建器——Knex.js,用 JS 链式调用拼 SQL,不用手写原始 SQL 字符串。
3. ORM——Prisma、Sequelize、TypeORM。把数据库表映射成 JS 类/对象,用面向对象的方式操作数据库。
多数现代 Node.js 项目会用 ORM 或至少查询构建器——手写 SQL 虽然灵活但容易注入、不易维护。但关键的性能查询仍然可能需要原生 SQL。
bash
# 安装常用数据库驱动
npm install mysql2 # MySQL
npm install pg # PostgreSQL
npm install mongodb # MongoDB
npm install prisma @prisma/client # Prisma ORM
npm install knex # Knex 查询构建器02. MySQL——mysql2 驱动
mysql2 是 Node.js 连接 MySQL 最常用的驱动。它支持 Promise API、连接池、预处理语句(防 SQL 注入)。
连接池是生产环境必备——每次请求创建连接太浪费,连接池维护一组复用连接。mysql2/promise 提供 async/await 友好的 API。
预处理语句(Prepared Statement)用 ? 占位符,防止 SQL 注入。mysql2.execute('SELECT * FROM users WHERE id = ?', [userId]) 自动转义参数。
事务用连接池的 getConnection 拿到一个连接,用 beginTransaction、commit、rollback 管控。事务结束后 connection.release() 还回连接池。
javascript
const mysql = require('mysql2/promise');
// 连接池
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
database: 'mydb',
waitForConnections: true,
connectionLimit: 10,
});
// 查询
const [rows] = await pool.execute(
'SELECT * FROM users WHERE age > ?', [18]
);
// 插入
const [result] = await pool.execute(
'INSERT INTO users (name, email) VALUES (?, ?)',
['Alice', '[email protected]']
);
console.log(result.insertId);
// 事务
const conn = await pool.getConnection();
try {
await conn.beginTransaction();
await conn.execute('UPDATE accounts SET balance = balance - ? WHERE id = ?', [100, 1]);
await conn.execute('UPDATE accounts SET balance = balance + ? WHERE id = ?', [100, 2]);
await conn.commit();
} catch (err) {
await conn.rollback();
throw err;
} finally {
conn.release();
}连接池的 connectionLimit 不是越大越好——MySQL 默认最大连接数 151。设太大会耗尽 MySQL 的连接资源。
03. PostgreSQL——pg 驱动与连接池
pg 是 Node.js 连接 PostgreSQL 的标准驱动。跟 mysql2 类似但 PG 特有的功能:
pg.Pool 管理连接池。参数化查询用 $1、$2 占位符(不是 ?)。
PG 支持 LISTEN/NOTIFY——数据库能主动推送消息给 Node.js。适合实时更新场景。
JSONB 查询的结果自动解析为 JS 对象,不需要额外处理。PG 的 COPY 命令可以做高速批量插入。
pg-promise 是 pg 的 Promise 封装,提供更丰富的 API。但原生的 pg 已经支持 Promise 和 async/await,不一定需要额外库。
javascript
const { Pool } = require('pg');
const pool = new Pool({
host: 'localhost',
database: 'mydb',
user: 'postgres',
max: 10,
});
// 查询
const { rows } = await pool.query(
'SELECT * FROM users WHERE age > $1', [18]
);
// JSONB 查询
const { rows } = await pool.query(
"SELECT * FROM products WHERE attributes @> $1",
[{ color: 'blue' }]
);
// attributes 字段自动从 JSONB 转为 JS 对象
// LISTEN/NOTIFY
const client = await pool.connect();
await client.query('LISTEN new_order');
client.on('notification', (msg) => {
console.log('New order:', msg.payload);
});pg 的 $1 占位符是 PostgreSQL 特有的参数化方式。记住不要拼字符串传参——参数化既防注入又让数据库缓存执行计划。
04. MongoDB——mongodb 驱动
mongodb 是 MongoDB 官方 Node.js 驱动。它操作 MongoDB 的 JSON 文档风格跟写 JavaScript 对象一样自然。
MongoClient.connect 创建连接(通常是全局单例)。db.collection 获取集合引用。然后就是 find、insertOne、updateOne、deleteMany 这些操作。
MongoDB 驱动返回的是 Cursor 对象——惰性加载,不会一次性把全部数据拉回来。用 .toArray() 转数组或用 for await...of 迭代。
跟关系型数据库不同,MongoDB 不需要预定义 schema。但你可以用 JSON Schema Validation 在集合上定义校验规则——在 driver 这层不用管,但好的实践是配合 Mongoose 加 schema。
javascript
const { MongoClient } = require('mongodb');
const client = new MongoClient('mongodb://localhost:27017');
await client.connect();
const db = client.db('mydb');
const users = db.collection('users');
// 查询
const adults = await users.find({ age: { $gt: 18 } }).toArray();
// 插入
const result = await users.insertOne({
name: 'Alice',
email: '[email protected]',
age: 36
});
console.log(result.insertedId);
// 游标迭代(大数据集)
const cursor = users.find({ age: { $gt: 18 } });
for await (const doc of cursor) {
console.log(doc.name);
}05. ORM 选型——Prisma vs Sequelize vs Knex
Prisma——新一代 ORM,强类型是其最大卖点。定义 schema 文件(schema.prisma),一键生成 TypeScript 类型和客户端代码。类型安全贯穿所有查询,IDE 自动补全完美。适合 TypeScript 项目。
Sequelize——最老牌的 Node.js ORM。支持 MySQL、PG、SQLite、MSSQL。功能全面但 API 设计较老(回调风格为主),TypeScript 支持一般。适合老项目或不需要强类型的团队。
Knex——查询构建器,不是完全体 ORM。在原始 SQL 和 ORM 之间——链式调用生成 SQL 但不会把结果映射成 Model 对象。适合需要写复杂 SQL 但又不想管理原始字符串的场景。
选型建议:新项目用 Prisma(TypeScript 加持),需要灵活控制的用 Knex,历史遗留项目看看 Sequelize。
javascript
// Prisma——定义 schema
// schema.prisma
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
// 使用 Prisma Client
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const users = await prisma.user.findMany({
where: { age: { gt: 18 } },
include: { posts: true }
});
// Knex——查询构建器
const knex = require('knex')({ client: 'pg', connection: {...} });
const users = await knex('users')
.where('age', '>', 18)
.orderBy('created_at', 'desc')
.limit(10);Prisma 的编译器模型——你改 schema 后跑 prisma generate,它重新生成类型安全的 client。所有查询都有编译时类型检查,运行时少很多 bug。
知识测验
第 1/5 题正确 0
Node.js 连接 MySQL 推荐用哪个驱动?
下一节
下一节 WebSocket 实时通信