Sequelize ORM 入门:Node.js 中的 SQL 操作
什么是 Sequelize ORM?
Sequelize 是一款基于 Promise 的 Node.js ORM(对象关系映射)工具,支持 PostgreSQL、MySQL、MariaDB、SQLite 以及 Microsoft SQL Server。它让你能够用 JavaScript 对象的方式操作关系型数据库,从而避免手写大量 SQL 语句。对于初学者,Sequelize 可以显著降低数据库操作的门槛,提供模型定义、关联、事务、数据校验和迁移等完整功能。
为什么选择 Sequelize?
- 多数据库支持:一套代码即可切换不同数据库后端。
- 模型驱动的开发:通过定义模型来映射数据库表,结构清晰。
- 关联管理:简化表之间的关系处理(一对一、一对多、多对多)。
- 内置校验与钩子:支持字段验证、生命周期钩子(如 beforeCreate、afterDestroy)。
- 迁移系统:版本化管理数据库结构变更,便于团队协作。
- 活跃社区:文档完善、生态扩展丰富(如 sequelize-typescript、sequelize-cli)。
环境准备与安装
在开始之前,确保已安装 Node.js (≥ v14) 和 npm/yarn,以及你选择的数据库(例如本地运行的 MySQL)。
创建项目并安装依赖
mkdir sequelize-demo && cd sequelize-demo
npm init -y
npm install sequelize
根据你使用的数据库,还需安装对应的数据库驱动:
- MySQL/MariaDB:
npm install mysql2 - PostgreSQL:
npm install pg pg-hstore - SQLite:
npm install sqlite3 - MSSQL:
npm install tedious
本教程以 MySQL 为例,同时安装 mysql2。
快速连接到数据库
创建 db.js 文件,配置 Sequelize 实例。
// db.js
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize('database_name', 'username', 'password', {
host: 'localhost',
dialect: 'mysql', // 或 'postgres', 'sqlite', 'mssql'
logging: false, // 关闭 SQL 日志输出,可在调试时设为 console.log
});
module.exports = sequelize;
测试连接:
// test-connection.js
const sequelize = require('./db');
(async () => {
try {
await sequelize.authenticate();
console.log('数据库连接成功');
} catch (error) {
console.error('连接失败:', error);
}
})();
运行 node test-connection.js,若输出“数据库连接成功”则配置正确。
定义模型 (Model)
模型是与数据库表对应的抽象。Sequelize 提供两种定义方式:调用 sequelize.define 或继承 Model 类。推荐使用更现代的类继承方式。
使用类继承定义模型
以简单的“用户”表为例:
// models/User.js
const { DataTypes, Model } = require('sequelize');
const sequelize = require('../db');
class User extends Model {}
User.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
validate: {
notEmpty: true, // 不允许空字符串
},
},
email: {
type: DataTypes.STRING(100),
allowNull: false,
unique: true,
validate: {
isEmail: true, // 内建 Email 校验
},
},
age: {
type: DataTypes.INTEGER,
validate: {
min: 0,
max: 150,
},
},
}, {
sequelize, // 传入连接实例
modelName: 'User', // 模型名称
tableName: 'users', // 数据库表名(可选,默认为模型名的复数形式)
timestamps: true, // 自动添加 createdAt 和 updatedAt
});
module.exports = User;
同步模型到数据库
在应用中调用 sequelize.sync() 可自动创建表(开发环境使用,生产环境推荐迁移)。
// app.js
const sequelize = require('./db');
const User = require('./models/User');
(async () => {
await sequelize.sync({ force: false }); // force: true 会先删除表再重建
console.log('所有模型已同步');
})();
CRUD 基础操作
创建记录 (Create)
const newUser = await User.create({
name: '张三',
email: 'zhangsan@example.com',
age: 28
});
console.log(newUser.toJSON()); // 输出新用户数据
查询记录 (Read)
// 查询所有用户
const allUsers = await User.findAll();
// 按条件查询单条
const user = await User.findOne({ where: { email: 'zhangsan@example.com' } });
// 按主键查询
const userById = await User.findByPk(1);
// 使用特定字段过滤和排序
const filteredUsers = await User.findAll({
where: { age: { [Op.gt]: 20 } }, // 年龄大于 20 需导入 Op
order: [['name', 'ASC']],
limit: 5,
});
别忘了导入 Op 用于高级操作符:
const { Op } = require('sequelize');
常用操作符:[Op.gt] (大于), [Op.lt] (小于), [Op.like] (模糊), [Op.in] (包含) 等。
更新记录 (Update)
// 更新单个字段
await User.update({ age: 29 }, { where: { id: 1 } });
// 通过实例更新(推荐,会触发钩子和校验)
const user = await User.findByPk(1);
if (user) {
user.age = 30;
await user.save();
}
删除记录 (Delete)
// 直接删除
await User.destroy({ where: { id: 1 } });
// 或通过实例
const user = await User.findByPk(2);
if (user) await user.destroy();
模型关联 (Associations)
Sequelize 提供四种关联类型:HasOne、BelongsTo、HasMany、BelongsToMany。
一对一 (One-to-One)
假设每个用户对应一个个人资料 (Profile):
// models/Profile.js
class Profile extends Model {}
Profile.init({
bio: DataTypes.TEXT,
avatar: DataTypes.STRING,
}, { sequelize, modelName: 'Profile' });
// 定义关联
User.hasOne(Profile, { foreignKey: 'userId' });
Profile.belongsTo(User, { foreignKey: 'userId' });
查询时包含关联数据:
const userWithProfile = await User.findByPk(1, {
include: Profile
});
一对多 (One-to-Many)
用户与文章 (Post) 的关系:
User.hasMany(Post, { foreignKey: 'userId' });
Post.belongsTo(User, { foreignKey: 'userId' });
获取用户的所有文章:
const user = await User.findByPk(1, { include: Post });
console.log(user.Posts); // 别名默认大写,可通过 as 设置
多对多 (Many-to-Many)
用户与角色 (Role) 通过中间表 user_roles 关联:
User.belongsToMany(Role, { through: 'user_roles' });
Role.belongsToMany(User, { through: 'user_roles' });
添加关联:user.addRole(role),查询关联:user.getRoles()。
使用迁移 (Migrations)
迁移用于版本化数据库结构变更,适合团队协作和生产环境。使用 sequelize-cli 工具管理。
安装与初始化
npm install --save-dev sequelize-cli
npx sequelize-cli init
初始化后会生成 config、models、migrations、seeders 目录。
配置数据库连接
编辑 config/config.json,配置不同环境(development, test, production)的数据库信息。
创建迁移文件
npx sequelize-cli migration:generate --name create-users-table
在生成的迁移文件中编写“升级”和“降级”逻辑:
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('users', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
email: Sequelize.STRING,
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE
});
},
down: async (queryInterface) => {
await queryInterface.dropTable('users');
}
};
执行迁移
npx sequelize-cli db:migrate
回滚上一次迁移:npx sequelize-cli db:migrate:undo。
实用技巧与常见问题
数据校验
Sequelize 允许在模型定义中配置校验,也可自定义校验器:
email: {
type: DataTypes.STRING,
validate: {
isEmail: true,
myCustomValidator(value) {
if (!value.endsWith('@company.com')) {
throw new Error('必须使用公司邮箱');
}
}
}
}
钩子 (Hooks)
在数据生命周期不同阶段自动执行逻辑,例如密码加密:
User.addHook('beforeCreate', async (user) => {
user.password = await hashPassword(user.password);
});
事务 (Transactions)
保证多个操作原子性:
const t = await sequelize.transaction();
try {
const user = await User.create({ name: '李四' }, { transaction: t });
await user.createProfile({ bio: 'hello' }, { transaction: t });
await t.commit();
} catch (error) {
await t.rollback();
}
原始查询与复杂查询
偶尔需要手写 SQL,可使用 sequelize.query:
const [results, metadata] = await sequelize.query(
'SELECT * FROM users WHERE age > :age',
{
replacements: { age: 25 },
type: QueryTypes.SELECT
}
);
也可利用 Sequelize 的 literal 或聚合函数如 fn、col 构建复杂查询。
总结
本教程带你从零搭建了 Sequelize ORM 的开发环境,涵盖了连接数据库、定义模型、CRUD 操作、关联关系以及迁移系统的使用。通过 ORM,你可以用更符合 JavaScript 习惯的方式管理数据库逻辑,显著提升开发效率和可维护性。作为下一步,建议深入学习 Sequelize 的查询作用域 (Scopes)、多态关联、性能优化以及结合 Express 构建完整的 RESTful API。
祝你写码愉快!