Node系列 · ORM:Sequelize 简介
ORM(Object-Relational Mapping)把"数据库表行"映射成"JS 对象",让开发者用面向对象的方式操作数据,而不必每次写 SQL。Sequelize 是 Node 生态最成熟的 ORM------本文讲清楚它的核心概念和适用边界。
一、ORM 是什么
ORM(对象关系映射)解决一个根本问题:JS 世界是对象,数据库世界是表和行------怎么让两者无缝对接?
#mermaid-svg-CYspGbXbek3VnPba{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CYspGbXbek3VnPba .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CYspGbXbek3VnPba .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CYspGbXbek3VnPba .error-icon{fill:#552222;}#mermaid-svg-CYspGbXbek3VnPba .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CYspGbXbek3VnPba .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CYspGbXbek3VnPba .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CYspGbXbek3VnPba .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CYspGbXbek3VnPba .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CYspGbXbek3VnPba .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CYspGbXbek3VnPba .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CYspGbXbek3VnPba .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CYspGbXbek3VnPba .marker.cross{stroke:#333333;}#mermaid-svg-CYspGbXbek3VnPba svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CYspGbXbek3VnPba p{margin:0;}#mermaid-svg-CYspGbXbek3VnPba .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CYspGbXbek3VnPba .cluster-label text{fill:#333;}#mermaid-svg-CYspGbXbek3VnPba .cluster-label span{color:#333;}#mermaid-svg-CYspGbXbek3VnPba .cluster-label span p{background-color:transparent;}#mermaid-svg-CYspGbXbek3VnPba .label text,#mermaid-svg-CYspGbXbek3VnPba span{fill:#333;color:#333;}#mermaid-svg-CYspGbXbek3VnPba .node rect,#mermaid-svg-CYspGbXbek3VnPba .node circle,#mermaid-svg-CYspGbXbek3VnPba .node ellipse,#mermaid-svg-CYspGbXbek3VnPba .node polygon,#mermaid-svg-CYspGbXbek3VnPba .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CYspGbXbek3VnPba .rough-node .label text,#mermaid-svg-CYspGbXbek3VnPba .node .label text,#mermaid-svg-CYspGbXbek3VnPba .image-shape .label,#mermaid-svg-CYspGbXbek3VnPba .icon-shape .label{text-anchor:middle;}#mermaid-svg-CYspGbXbek3VnPba .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CYspGbXbek3VnPba .rough-node .label,#mermaid-svg-CYspGbXbek3VnPba .node .label,#mermaid-svg-CYspGbXbek3VnPba .image-shape .label,#mermaid-svg-CYspGbXbek3VnPba .icon-shape .label{text-align:center;}#mermaid-svg-CYspGbXbek3VnPba .node.clickable{cursor:pointer;}#mermaid-svg-CYspGbXbek3VnPba .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CYspGbXbek3VnPba .arrowheadPath{fill:#333333;}#mermaid-svg-CYspGbXbek3VnPba .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CYspGbXbek3VnPba .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CYspGbXbek3VnPba .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CYspGbXbek3VnPba .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CYspGbXbek3VnPba .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CYspGbXbek3VnPba .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CYspGbXbek3VnPba .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CYspGbXbek3VnPba .cluster text{fill:#333;}#mermaid-svg-CYspGbXbek3VnPba .cluster span{color:#333;}#mermaid-svg-CYspGbXbek3VnPba div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CYspGbXbek3VnPba .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CYspGbXbek3VnPba rect.text{fill:none;stroke-width:0;}#mermaid-svg-CYspGbXbek3VnPba .icon-shape,#mermaid-svg-CYspGbXbek3VnPba .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CYspGbXbek3VnPba .icon-shape p,#mermaid-svg-CYspGbXbek3VnPba .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CYspGbXbek3VnPba .icon-shape .label rect,#mermaid-svg-CYspGbXbek3VnPba .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CYspGbXbek3VnPba .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CYspGbXbek3VnPba .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CYspGbXbek3VnPba :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据库世界
JS 世界
ORM 映射
user.id → row.id
user.name → row.name
user.save → INSERT
User.findAll → SELECT
const user = { id: 1, name: 'Alice' }
users 表 id=1 name='Alice'
ORM 的核心抽象:
- 模型(Model) = 表(Table)
- 实例(Instance) = 行(Row)
- 属性(Attribute) = 列(Column)
二、ORM 的核心价值
| 价值 | 说明 |
|---|---|
| 屏蔽 SQL | 不需要拼接 SQL,开发者用 JS 方法操作数据 |
| 类型映射 | DataTypes.STRING / DataTypes.INTEGER 等统一类型定义 |
| 模型关联 | hasMany / belongsTo 一行声明外键关系 |
| 数据迁移 | 通过 migrations 管理 schema 变更 |
| 数据库无关 | 同一份代码可以切换 MySQL / PostgreSQL / SQLite |
三、Node 中的 ORM 选项
| ORM | 风格 | 适用 |
|---|---|---|
| Sequelize | Promise / 类模型 | JS 项目首选 |
| Sequelize(TS) | 同上 + 类型 | TS 项目 |
| TypeORM | 装饰器(TS) | TS 项目 |
| Prisma | Schema 文件 + 生成 | TS 项目,现代推荐 |
| Knex | Query Builder(半 ORM) | 需要手写 SQL 的场景 |
| Drizzle | TS 类型安全 + 轻量 ORM | 新兴 |
::: tip
本指南聚焦 Sequelize ------它在 Node 生态中历史最久、社区最成熟、文档最完整。新项目如果想用 TS-first 体验,可以选 Prisma / Drizzle。
:::
四、安装与连接
bash
npm install sequelize mysql2
mysql2 是 Sequelize 操作 MySQL 的底层驱动;不能少。
javascript:sequelize-connect.js
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize('myapp', 'root', 'your-password', {
host: '127.0.0.1',
port: 3306,
dialect: 'mysql', // 数据库类型
logging: false, // 设为 true 看 SQL 日志
pool: {
max: 10,
min: 0,
acquire: 30000,
idle: 10000,
},
});
// 测试连接
await sequelize.authenticate();
console.log('连接成功');
支持的 dialect:
mysql/mariadbpostgressqlite/mssqldb2/oracle
五、核心概念
5.1 DataTypes
Sequelize 用 DataTypes 描述列类型:
javascript:sequelize-connect.js
const { DataTypes } = require('sequelize');
const User = sequelize.define('User', {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
name: { type: DataTypes.STRING(50), allowNull: false },
email: { type: DataTypes.STRING(100), allowNull: false, unique: true },
age: { type: DataTypes.INTEGER, defaultValue: 0 },
isActive: { type: DataTypes.BOOLEAN, defaultValue: true },
bio: { type: DataTypes.TEXT },
birthDate: { type: DataTypes.DATEONLY },
metadata: { type: DataTypes.JSON },
});
| DataTypes | 对应 MySQL |
|---|---|
STRING |
VARCHAR(255) |
TEXT |
TEXT |
INTEGER |
INT |
BIGINT |
BIGINT |
FLOAT / DOUBLE |
FLOAT / DOUBLE |
DECIMAL(p, s) |
DECIMAL |
BOOLEAN |
TINYINT(1) |
DATE |
DATETIME |
DATEONLY |
DATE |
JSON |
JSON |
UUID |
CHAR(36) |
5.2 Model(模型)
模型 = 表的抽象。每个 sequelize.define() 返回一个 Model 类。
javascript:sequelize-query.js
const User = sequelize.define('User', { /* columns */ });
// 默认表名是 model 名复数(User → users)
// 可显式指定:tableName: 'my_users'
5.3 Instance(实例)
实例 = 一行数据。Model.create() 返回 Instance,Model.findOne() 等也返回 Instance:
javascript:sequelize-query.js
const user = await User.create({
name: 'Alice',
email: 'alice@example.com',
});
// 实例属性就是行字段
console.log(user.id, user.name, user.email);
// 修改并保存
user.name = 'Alice2';
await user.save();
// 删除
await user.destroy();
5.4 查询接口
javascript:sequelize-query.js
const { Op } = require('sequelize');
// 查询一条
const user = await User.findOne({ where: { id: 1 } });
// 按主键查
const user = await User.findByPk(1);
// 查询多条
const users = await User.findAll({
where: { isActive: true },
order: [['id', 'DESC']],
limit: 10,
});
// 计数
const count = await User.count({ where: { age: { [Op.gt]: 18 } } });
六、ORM 与原始 SQL 的取舍
| 维度 | 原始 SQL(mysql2) | ORM(Sequelize) |
|---|---|---|
| 性能 | 最优(无中间层) | 略低(多了对象转换) |
| 可读性 | 复杂 JOIN 易乱 | 模型方法直观 |
| 类型安全 | 字符串 SQL,无类型 | TS 项目类型完整 |
| 迁移 | 自己管 | 有 migrations 工具 |
| 跨数据库 | 锁死 MySQL 方言 | 一份代码换 PG/SQLite |
| 复杂查询 | 任意 SQL | 部分场景要 raw SQL |
| 学习成本 | 会 SQL 即可 | 概念多,模型 + 关联 + 迁移 |
::: tip
ORM 不万能 。遇到特别复杂的报表查询(多层嵌套、子查询、窗口函数),直接 sequelize.query(sql, { type: SELECT }) 跑 raw SQL 更简单。
:::
七、连接管理
javascript:sequelize-manage.js
// 应用退出时关闭连接
process.on('SIGTERM', async () => {
await sequelize.close();
process.exit(0);
});
不关连接会让 MySQL 的 Sleep 连接堆积,触发 Too many connections。
八、小结
- ORM 把"表 → 模型 / 行 → 实例 / 列 → 属性",让 JS 开发者用面向对象方式操作数据
- Sequelize 是 Node 生态最成熟的 ORM;新项目首选,TS 项目可考虑 Prisma
- 核心概念:
DataTypes(类型)、Model(表)、Instance(行)、sequelize.query()(底层 SQL 入口) - 永远用占位符或 ORM API,不要拼 SQL
- 复杂查询可以用
sequelize.query()跑 raw SQL,不被 ORM 束缚 - 应用退出时
sequelize.close()释放连接