Sequelize - Node.js ORM框架
1. 什么是Sequelize?
Sequelize是一个基于Promise的Node.js ORM(对象关系映射)框架,支持PostgreSQL、MySQL、MariaDB、SQLite和Microsoft SQL Server等多种关系型数据库。它提供了丰富的功能,包括模型定义、关联关系、事务、迁移、种子数据等,使得数据库操作更加直观和高效。
1.1 核心特性
- 支持多种数据库:PostgreSQL、MySQL、MariaDB、SQLite、Microsoft SQL Server
- 基于Promise的API:支持async/await语法
- 自动数据库迁移:管理数据库架构变更
- 模型关联:支持一对一、一对多、多对多关系
- 事务支持:确保数据一致性
- 数据验证:内置验证规则
- 钩子函数:支持模型生命周期事件
- 查询构建器:灵活的查询API
2. 快速开始
2.1 安装
# 安装Sequelize核心包
npm install sequelize
# 安装相应数据库的驱动
# 例如,MySQL
npm install mysql2
# 或PostgreSQL
npm install pg pg-hstore
# 或SQLite
npm install sqlite3
# 或Microsoft SQL Server
npm install tedious2.2 基本配置
const { Sequelize } = require('sequelize');
// 方法1: 单独传递参数
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql' /* 选择 'mysql' | 'mariadb' | 'postgres' | 'mssql' 其一 */
});
// 方法2: 使用连接URL
const sequelize = new Sequelize('mysql://username:password@localhost:3306/database');
// 测试连接
async function testConnection() {
try {
await sequelize.authenticate();
console.log('连接成功');
} catch (error) {
console.error('连接失败:', error);
}
}
testConnection();3. 模型定义
3.1 基本模型
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('mysql://root:password@localhost:3306/test');
// 定义模型
const User = sequelize.define('User', {
// 模型属性
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
age: {
type: DataTypes.INTEGER,
defaultValue: 18
},
createdAt: {
type: DataTypes.DATE,
defaultValue: Sequelize.NOW
}
}, {
// 模型选项
tableName: 'users', // 自定义表名
timestamps: true // 自动添加createdAt和updatedAt字段
});
// 同步模型到数据库
async function syncModel() {
await User.sync({ force: false }); // force: true会删除现有表并重新创建
console.log('User模型已同步');
}
syncModel();3.2 数据类型
Sequelize支持多种数据类型:
- 字符串类型:STRING, TEXT, TINYTEXT, MEDIUMTEXT, LONGTEXT
- 数字类型:INTEGER, BIGINT, FLOAT, DOUBLE, DECIMAL
- 布尔类型:BOOLEAN
- 日期类型:DATE, DATEONLY
- 二进制类型:BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB
- 枚举类型:ENUM
- 数组类型:ARRAY (仅PostgreSQL支持)
- JSON类型:JSON, JSONB (仅PostgreSQL支持)
4. CRUD操作
4.1 创建记录
// 方法1: 使用create()
async function createUser() {
try {
const user = await User.create({
name: '张三',
email: 'zhangsan@example.com',
age: 30
});
console.log('创建的用户:', user.toJSON());
} catch (error) {
console.error('创建用户失败:', error);
}
}
// 方法2: 先构建实例,再保存
async function buildAndSaveUser() {
const user = User.build({
name: '李四',
email: 'lisi@example.com',
age: 25
});
await user.save();
console.log('保存的用户:', user.toJSON());
}
createUser();
buildAndSaveUser();4.2 查询记录
// 查找所有记录
async function findAllUsers() {
const users = await User.findAll();
console.log('所有用户:', users.map(user => user.toJSON()));
}
// 按条件查找
async function findUsersByAge() {
const users = await User.findAll({
where: {
age: {
[Sequelize.Op.gt]: 25 // 年龄大于25
}
}
});
console.log('年龄大于25的用户:', users.map(user => user.toJSON()));
}
// 查找单个记录
async function findOneUser() {
const user = await User.findOne({
where: {
email: 'zhangsan@example.com'
}
});
console.log('找到的用户:', user ? user.toJSON() : '未找到');
}
// 按主键查找
async function findUserById() {
const user = await User.findByPk(1);
console.log('按ID找到的用户:', user ? user.toJSON() : '未找到');
}
findAllUsers();
findUsersByAge();
findOneUser();
findUserById();4.3 更新记录
// 方法1: 更新单个实例
async function updateUser() {
const user = await User.findByPk(1);
if (user) {
user.name = '张三更新';
user.age = 31;
await user.save();
console.log('更新后的用户:', user.toJSON());
}
}
// 方法2: 批量更新
async function updateMultipleUsers() {
const result = await User.update(
{ age: 20 },
{
where: {
age: {
[Sequelize.Op.lt]: 18
}
}
}
);
console.log('更新的行数:', result[0]);
}
updateUser();
updateMultipleUsers();4.4 删除记录
// 方法1: 删除单个实例
async function deleteUser() {
const user = await User.findByPk(1);
if (user) {
await user.destroy();
console.log('用户已删除');
}
}
// 方法2: 批量删除
async function deleteMultipleUsers() {
const result = await User.destroy({
where: {
age: {
[Sequelize.Op.gt]: 60
}
}
});
console.log('删除的行数:', result);
}
deleteUser();
deleteMultipleUsers();5. 模型关联
5.1 一对一关系
// 定义Profile模型
const Profile = sequelize.define('Profile', {
bio: DataTypes.TEXT,
avatar: DataTypes.STRING
});
// 定义User和Profile的一对一关系
User.hasOne(Profile, {
foreignKey: 'userId',
as: 'profile'
});
Profile.belongsTo(User, {
foreignKey: 'userId',
as: 'user'
});
// 创建带关联的用户
async function createUserWithProfile() {
const user = await User.create({
name: '王五',
email: 'wangwu@example.com',
profile: {
bio: '这是个人简介',
avatar: 'avatar.jpg'
}
}, {
include: 'profile'
});
console.log('创建的用户及简介:', user.toJSON());
}
// 查询带关联的用户
async function findUserWithProfile() {
const user = await User.findByPk(1, {
include: 'profile'
});
console.log('用户及简介:', user.toJSON());
}
createUserWithProfile();
findUserWithProfile();5.2 一对多关系
// 定义Post模型
const Post = sequelize.define('Post', {
title: DataTypes.STRING,
content: DataTypes.TEXT
});
// 定义User和Post的一对多关系
User.hasMany(Post, {
foreignKey: 'userId',
as: 'posts'
});
Post.belongsTo(User, {
foreignKey: 'userId',
as: 'user'
});
// 创建带关联的用户和帖子
async function createUserWithPosts() {
const user = await User.create({
name: '赵六',
email: 'zhaoliu@example.com',
posts: [
{
title: '第一篇帖子',
content: '帖子内容1'
},
{
title: '第二篇帖子',
content: '帖子内容2'
}
]
}, {
include: 'posts'
});
console.log('创建的用户及帖子:', user.toJSON());
}
// 查询带关联的用户和帖子
async function findUserWithPosts() {
const user = await User.findByPk(1, {
include: 'posts'
});
console.log('用户及帖子:', user.toJSON());
}
createUserWithPosts();
findUserWithPosts();5.3 多对多关系
// 定义Tag模型
const Tag = sequelize.define('Tag', {
name: DataTypes.STRING
});
// 定义Post和Tag的多对多关系(通过中间表)
const PostTag = sequelize.define('PostTag', {}, {
timestamps: false
});
Post.belongsToMany(Tag, {
through: PostTag,
as: 'tags',
foreignKey: 'postId'
});
Tag.belongsToMany(Post, {
through: PostTag,
as: 'posts',
foreignKey: 'tagId'
});
// 创建带标签的帖子
async function createPostWithTags() {
const post = await Post.create({
title: '带标签的帖子',
content: '帖子内容',
tags: [
{ name: '技术' },
{ name: '教程' }
]
}, {
include: 'tags'
});
console.log('创建的帖子及标签:', post.toJSON());
}
// 查询带标签的帖子
async function findPostWithTags() {
const post = await Post.findByPk(1, {
include: 'tags'
});
console.log('帖子及标签:', post.toJSON());
}
createPostWithTags();
findPostWithTags();6. 事务
6.1 基本事务
async function transferMoney() {
const t = await sequelize.transaction();
try {
// 从用户1扣款
const user1 = await User.findByPk(1, { transaction: t });
user1.balance -= 100;
await user1.save({ transaction: t });
// 给用户2加款
const user2 = await User.findByPk(2, { transaction: t });
user2.balance += 100;
await user2.save({ transaction: t });
// 提交事务
await t.commit();
console.log('转账成功');
} catch (error) {
// 回滚事务
await t.rollback();
console.error('转账失败:', error);
}
}
transferMoney();6.2 自动事务(使用回调)
async function autoTransaction() {
await sequelize.transaction(async (t) => {
// 所有操作都在事务中执行
const user1 = await User.findByPk(1, { transaction: t });
user1.balance -= 50;
await user1.save({ transaction: t });
const user2 = await User.findByPk(2, { transaction: t });
user2.balance += 50;
await user2.save({ transaction: t });
});
console.log('自动事务执行成功');
}
autoTransaction();7. 迁移
7.1 安装迁移工具
npm install --save-dev sequelize-cli7.2 初始化迁移配置
npx sequelize-cli init这会创建以下目录结构:
- config: 数据库配置文件
- models: 模型文件
- migrations: 迁移文件
- seeders: 种子文件
7.3 创建迁移文件
npx sequelize-cli model:generate --name User --attributes name:string,email:string,age:integer7.4 运行迁移
# 运行所有待执行的迁移
npx sequelize-cli db:migrate
# 回滚上一次迁移
npx sequelize-cli db:migrate:undo
# 回滚所有迁移
npx sequelize-cli db:migrate:undo:all7.5 示例迁移文件
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING,
unique: true
},
age: {
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Users');
}
};8. 种子数据
8.1 创建种子文件
npx sequelize-cli seed:generate --name demo-user8.2 运行种子
# 运行所有种子
npx sequelize-cli db:seed:all
# 回滚上一次种子
npx sequelize-cli db:seed:undo
# 回滚所有种子
npx sequelize-cli db:seed:undo:all8.3 示例种子文件
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.bulkInsert('Users', [
{
name: '管理员',
email: 'admin@example.com',
age: 30,
createdAt: new Date(),
updatedAt: new Date()
},
{
name: '测试用户',
email: 'test@example.com',
age: 25,
createdAt: new Date(),
updatedAt: new Date()
}
], {});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.bulkDelete('Users', null, {});
}
};9. 钩子函数
9.1 模型钩子
// 在模型定义中添加钩子
const User = sequelize.define('User', {
// 属性定义
}, {
hooks: {
// 创建前
beforeCreate: (user) => {
console.log('创建用户前:', user.name);
},
// 创建后
afterCreate: (user) => {
console.log('创建用户后:', user.id);
},
// 更新前
beforeUpdate: (user) => {
console.log('更新用户前:', user.id);
},
// 更新后
afterUpdate: (user) => {
console.log('更新用户后:', user.id);
},
// 删除前
beforeDestroy: (user) => {
console.log('删除用户前:', user.id);
},
// 删除后
afterDestroy: (user) => {
console.log('删除用户后:', user.id);
}
}
});9.2 实例钩子
// 为实例添加钩子
User.prototype.beforeSave = function() {
console.log('保存实例前:', this.name);
};10. 数据验证
10.1 内置验证器
const User = sequelize.define('User', {
name: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: true,
len: [2, 50] // 长度在2-50之间
}
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true, // 邮箱格式验证
notEmpty: true
}
},
age: {
type: DataTypes.INTEGER,
validate: {
isInt: true, // 整数验证
min: 0, // 最小值
max: 120 // 最大值
}
},
password: {
type: DataTypes.STRING,
validate: {
len: [6, Infinity], // 最小长度6
is: /^[a-zA-Z0-9_]+$/ // 自定义正则验证
}
}
});10.2 自定义验证器
const User = sequelize.define('User', {
username: {
type: DataTypes.STRING,
validate: {
// 自定义验证函数
isUnique: async function(value) {
const user = await User.findOne({ where: { username: value } });
if (user) {
throw new Error('用户名已存在');
}
},
// 带参数的自定义验证
customValidator: function(value) {
if (value === 'admin') {
throw new Error('用户名不能为admin');
}
}
}
}
});11. 高级查询
11.1 聚合查询
// 计数
async function countUsers() {
const count = await User.count();
console.log('用户总数:', count);
}
// 按条件计数
async function countUsersByAge() {
const count = await User.count({
where: {
age: {
[Sequelize.Op.gt]: 25
}
}
});
console.log('年龄大于25的用户数:', count);
}
// 求和
async function sumAges() {
const sum = await User.sum('age');
console.log('年龄总和:', sum);
}
// 平均值
async function averageAge() {
const avg = await User.average('age');
console.log('平均年龄:', avg);
}
// 最大值
async function maxAge() {
const max = await User.max('age');
console.log('最大年龄:', max);
}
// 最小值
async function minAge() {
const min = await User.min('age');
console.log('最小年龄:', min);
}
countUsers();
countUsersByAge();
sumAges();
averageAge();
maxAge();
minAge();11.2 分组查询
async function groupByAge() {
const result = await User.findAll({
attributes: [
'age',
[Sequelize.fn('COUNT', Sequelize.col('id')), 'userCount']
],
group: ['age'],
order: [[Sequelize.fn('COUNT', Sequelize.col('id')), 'DESC']]
});
console.log('按年龄分组:', result.map(item => item.toJSON()));
}
groupByAge();11.3 子查询
async function subQuery() {
const subQuery = await User.findAll({
attributes: ['userId'],
where: {
age: {
[Sequelize.Op.gt]: 30
}
},
raw: true
});
const userIds = subQuery.map(item => item.userId);
const posts = await Post.findAll({
where: {
userId: {
[Sequelize.Op.in]: userIds
}
}
});
console.log('年龄大于30的用户的帖子:', posts.map(post => post.toJSON()));
}
subQuery();12. 性能优化
12.1 索引
const User = sequelize.define('User', {
// 属性定义
}, {
indexes: [
// 普通索引
{ fields: ['email'] },
// 复合索引
{ fields: ['name', 'age'] },
// 唯一索引
{ fields: ['username'], unique: true },
// 部分索引 (仅PostgreSQL支持)
{
fields: ['status'],
where: { status: 'active' }
}
]
});12.2 延迟加载
// 延迟加载关联数据
async function lazyLoad() {
const user = await User.findByPk(1);
// 当需要时才加载关联数据
const posts = await user.getPosts();
console.log('用户的帖子:', posts.map(post => post.toJSON()));
}
lazyLoad();12.3 批量操作
// 批量创建
async function bulkCreate() {
const users = await User.bulkCreate([
{ name: '用户1', email: 'user1@example.com' },
{ name: '用户2', email: 'user2@example.com' },
{ name: '用户3', email: 'user3@example.com' }
]);
console.log('批量创建的用户数:', users.length);
}
// 批量更新
async function bulkUpdate() {
await User.bulkCreate([
{ id: 1, name: '更新用户1' },
{ id: 2, name: '更新用户2' }
], {
updateOnDuplicate: ['name'] // 当主键冲突时更新name字段
});
console.log('批量更新完成');
}
bulkCreate();
bulkUpdate();13. 最佳实践
13.1 项目结构
src/
├── config/
│ └── database.js # 数据库配置
├── models/
│ ├── index.js # 模型索引
│ ├── user.js # 用户模型
│ ├── post.js # 帖子模型
│ └── tag.js # 标签模型
├── migrations/ # 迁移文件
├── seeders/ # 种子文件
├── controllers/ # 控制器
├── routes/ # 路由
└── app.js # 应用入口13.2 模型索引文件
// models/index.js
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const config = require('../config/database');
const sequelize = new Sequelize(config.database, config.username, config.password, config);
const db = {};
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;13.3 错误处理
// 全局错误处理中间件
app.use((err, req, res, next) => {
// 验证错误
if (err.name === 'SequelizeValidationError') {
const errors = err.errors.map(error => error.message);
return res.status(400).json({ errors });
}
// 唯一约束错误
if (err.name === 'SequelizeUniqueConstraintError') {
return res.status(400).json({ error: '该值已存在' });
}
// 外键约束错误
if (err.name === 'SequelizeForeignKeyConstraintError') {
return res.status(400).json({ error: '关联数据不存在' });
}
// 其他错误
console.error(err);
res.status(500).json({ error: '服务器内部错误' });
});13.4 环境配置
// config/database.js
require('dotenv').config();
module.exports = {
development: {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.DB_HOST,
dialect: process.env.DB_DIALECT
},
test: {
username: process.env.TEST_DB_USERNAME,
password: process.env.TEST_DB_PASSWORD,
database: process.env.TEST_DB_NAME,
host: process.env.TEST_DB_HOST,
dialect: process.env.DB_DIALECT
},
production: {
username: process.env.PROD_DB_USERNAME,
password: process.env.PROD_DB_PASSWORD,
database: process.env.PROD_DB_NAME,
host: process.env.PROD_DB_HOST,
dialect: process.env.DB_DIALECT,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
}
};14. 总结
Sequelize是一个功能强大的Node.js ORM框架,它大大简化了与关系型数据库的交互。通过本教程的学习,您应该已经掌握了以下核心内容:
14.1 核心优势
- 简化数据库操作:通过面向对象的方式操作数据库
- 跨数据库兼容:同一套代码可以在不同数据库间切换
- 强大的关联支持:灵活处理复杂的数据关系
- 完善的工具链:包括迁移、种子、CLI工具等
- 现代JavaScript语法:支持Promise和async/await
14.2 适用场景
- Web应用后端:处理用户数据、内容管理等
- API服务:构建RESTful或GraphQL API
- 企业应用:需要复杂数据关系和事务支持的场景
- 数据迁移:管理数据库架构变更
14.3 最佳实践
- 合理设计模型:遵循数据库设计原则
- 使用迁移工具:管理数据库架构变更
- 优化查询:合理使用索引和预加载
- 错误处理:妥善处理数据库错误
- 环境配置:区分开发、测试和生产环境
Sequelize的强大功能和灵活性使其成为Node.js生态系统中最受欢迎的ORM框架之一。通过本教程的学习,您应该已经具备了在实际项目中使用Sequelize的能力,可以开始构建更加健壮和可维护的数据库应用了。