在这里插入图片描述

前言

在现代 Web 开发中,高效地管理数据库操作是每个 Node.js 开发者的必备技能。Sequelize 作为 Node.js 中最流行的 ORM 框架,为我们提供了强大的数据库操作能力。但很多开发者在面对 Sequelize 和 Sequelize-CLI 时常常感到困惑:它们到底是什么关系?应该如何正确使用?

本文将带你从零开始,全面掌握 Sequelize 的核心概念和使用方法,并通过实际示例展示完整的工作流程。

什么是 Sequelize?

核心概念解析

Sequelize 是一个基于 Promise 的 Node.js ORM(对象关系映射器),它支持多种数据库系统:

  • PostgreSQL
  • MySQL
  • MariaDB
  • SQLite
  • Microsoft SQL Server

Sequelize 的核心价值

// 传统 SQL 查询 vs Sequelize ORM

// 传统方式
const sql = 'SELECT * FROM users WHERE email = ?';
db.query(sql, [email], (error, results) => {
  // 处理结果
});

// Sequelize 方式
const user = await User.findOne({ 
  where: { email: email } 
});

Sequelize 的优势在于:

  • 安全性:自动防止 SQL 注入攻击
  • 可维护性:代码更清晰易读
  • 数据库无关性:轻松切换数据库系统
  • 类型安全:更好的开发体验

Sequelize 与 Sequelize-CLI 的关系

架构关系图

开发流程
迁移创建
模型设计
结构同步
数据填充
Express 应用
Sequelize ORM
Sequelize-CLI
数据库驱动
迁移管理
模型生成
种子数据
数据库配置
MySQL
PostgreSQL
SQLite
其他数据库
版本控制
模型模板
测试数据

职责分离

  • Sequelize: 运行时数据库操作

    • 模型实例管理
    • 查询构建
    • 关联关系处理
    • 事务管理
  • Sequelize-CLI: 开发时数据库管理

    • 项目脚手架
    • 迁移文件管理
    • 种子数据管理
    • 数据库状态维护

完整工作流程详解

项目初始化与配置

# 1. 创建项目结构
mkdir my-sequelize-app
cd my-sequelize-app

# 2. 安装依赖
npm init -y
npm install express sequelize mysql2
npm install --save-dev sequelize-cli nodemon

# 3. 初始化 Sequelize
npx sequelize-cli init

初始化后的目录结构:

config/
  config.json          # 数据库连接配置
models/
  index.js            # 模型加载入口
migrations/           # 迁移文件目录
seeders/              # 种子数据目录

数据库配置

{
  "development": {
    "username": "root",
    "password": null,
    "database": "database_development",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}

数据流与关系图

Express App Sequelize Model Query Interface Database 创建模型流程 定义模型结构 生成迁移文件 执行 SQL 迁移 确认表创建成功 数据操作流程 User.create() 构建 INSERT 查询 执行 SQL 插入 返回新记录 返回模型实例 查询流程 User.findAll() 构建 SELECT 查询 执行 SQL 查询 返回结果集 返回模型实例数组 Express App Sequelize Model Query Interface Database

实战示例:用户管理系统

1. 创建用户模型

npx sequelize-cli model:generate \
  --name User \
  --attributes name:string,email:string,age:integer

在这里插入图片描述

生成的模型文件 models/user.js

'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class User extends Model {
    static associate(models) {
      // 关联关系定义
    }
  }
  User.init({
    name: DataTypes.STRING,
    email: DataTypes.STRING,
    age: DataTypes.INTEGER
  }, {
    sequelize,
    modelName: 'User',
  });
  return User;
};

生成的迁移文件 migrations/xxxxxxxx-create-user.js

'use strict';
module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable('Users', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      name: {
        type: Sequelize.STRING
      },
      email: {
        type: Sequelize.STRING
      },
      age: {
        type: Sequelize.INTEGER
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  async down(queryInterface, Sequelize) {
    await queryInterface.dropTable('Users');
  }
};

2. 执行数据库迁移

# 创建数据库
npx sequelize-cli db:create

在这里插入图片描述

执行迁移

npx sequelize-cli db:migrate

在这里插入图片描述
说明:
对表的数据库中添另了,uer表,以有数据库迁移记录表(squelize-cli会使用它)
在这里插入图片描述

这条日志表明:

核心动作:成功地在开发环境的数据库中创建了一张名为 Users(大概率是这个名,根据迁移描述推断)的表。

执行状态:完全成功,没有出现任何错误。

影响:您的数据库结构发生了变更,现在应该存在一个由模型 User 对应的数据表。这张表很可能包含 id, username, email 等字段(具体字段取决于 20251113065809-create-user.js 迁移文件中的定义)。

后续:Sequelize 会在数据库中一个特殊的表(通常是 SequelizeMeta)里记录下 20251113065809-create-user 这个迁移的名字,以确保下次执行 db:migrate 时不会重复运行它。

简单来说,您成功地通过 Sequelize 迁移功能,为您的应用添加了用户表结构,项目现在可以向数据库中创建和存储用户数据了。

3. 添加种子数据

npx sequelize-cli seed:generate --name demo-users
# 执行后seedres目录会生成对应的种子数据脚本,需要修改文件内容,增加相关的数据的代码

种子文件 seeders/xxxxxxxx-demo-users.js
在这里插入图片描述

更新后的内容如下:

'use strict';

module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.bulkInsert('Users', [
      {
        name: '张三',
        email: 'zhangsan@example.com',
        age: 25,
        createdAt: new Date(),
        updatedAt: new Date()
      },
      {
        name: '李四',
        email: 'lisi@example.com', 
        age: 30,
        createdAt: new Date(),
        updatedAt: new Date()
      }
    ], {});
  },

  async down(queryInterface, Sequelize) {
    await queryInterface.bulkDelete('Users', null, {});
  }
};

运行种子数据:

npx sequelize-cli db:seed:all

在这里插入图片描述
执行后,uers表就多了相应的字段
在这里插入图片描述
以及SequelizeMeta表也多一条数据迁多的记录数据
在这里插入图片描述

4. 创建 Express 应用

const express = require('express');
const { User } = require('./models');

const app = express();
app.use(express.json());

// 获取所有用户
app.get('/users', async (req, res) => {
  try {
    const users = await User.findAll();
    res.json({ success: true, data: users });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

// 创建用户
app.post('/users', async (req, res) => {
  try {
    const { name, email, age } = req.body;
    const user = await User.create({ name, email, age });
    res.status(201).json({ success: true, data: user });
  } catch (error) {
    res.status(400).json({ success: false, error: error.message });
  }
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

模型演进:添加新字段

在实际开发中,需求变更导致数据库结构调整是很常见的。让我们看看如何安全地添加新字段。

1. 创建迁移文件

npx sequelize-cli migration:generate --name add-phone-to-users

在这里插入图片描述
同样生成对应的模块文件后,也添加自己的迁移逻辑
在这里插入图片描述

2. 编写迁移逻辑

'use strict';

module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.addColumn('Users', 'phone', {
      type: Sequelize.STRING,
      allowNull: true,
      after: 'email' // MySQL 特定语法
    });
    
    // 添加索引(可选)
    await queryInterface.addIndex('Users', ['phone']);
  },

  async down(queryInterface, Sequelize) {
    await queryInterface.removeColumn('Users', 'phone');
  }
};

3. 更新模型定义

// models/user.js
User.init({
  name: DataTypes.STRING,
  email: DataTypes.STRING,
  phone: DataTypes.STRING,  // 新增字段
  age: DataTypes.INTEGER
}, {
  sequelize,
  modelName: 'User',
});

4. 执行迁移

npx sequelize-cli db:migrate

在这里插入图片描述
成功执行后,表添加了对应的字段
在这里插入图片描述

完整系统架构图

开发工具链
结构变更
迁移文件
测试数据
种子文件
业务逻辑
模型定义
客户端请求
Express 路由
控制器
Sequelize 模型
Sequelize 实例
数据库驱动
物理数据库
Sequelize-CLI
迁移管理
种子管理
模型生成
版本控制表
初始数据

最佳实践与注意事项

1. 迁移文件管理

  • 每个变更一个迁移:保持迁移文件的原子性
  • 描述性命名:使用清晰的迁移文件名
  • 测试回滚:确保 down 函数正确工作

2. 模型设计原则

// 好的实践:详细的属性定义
User.init({
  name: {
    type: DataTypes.STRING,
    allowNull: false,
    validate: {
      notEmpty: true
    }
  },
  email: {
    type: DataTypes.STRING,
    allowNull: false,
    unique: true,
    validate: {
      isEmail: true
    }
  }
}, {
  sequelize,
  modelName: 'User',
});

3. 种子数据策略

  • 开发数据:为开发环境准备 realistic 数据
  • 测试数据:为测试环境准备 edge case 数据
  • 生产数据:必要的基础数据(如管理员账户)

4. 错误处理

// 在 Express 中的全局错误处理
app.use(async (err, req, res, next) => {
  // Sequelize 错误处理
  if (err.name === 'SequelizeValidationError') {
    const messages = err.errors.map(error => error.message);
    return res.status(400).json({
      success: false,
      errors: messages
    });
  }
  
  // 其他错误处理
  res.status(500).json({
    success: false,
    error: 'Internal server error'
  });
});

常见问题解答

Q: 什么时候应该使用迁移 vs 直接修改数据库?

A: 永远使用迁移!迁移文件提供了:

  • 版本控制的数据库结构
  • 团队协作的一致性
  • 可重复的部署流程
  • 安全的回滚机制

Q: Sequelize.sync() 和迁移有什么区别?

A:

  • sequelize.sync():开发时快速原型设计
  • 迁移:生产环境的数据库变更管理

Q: 如何处理复杂的数据库关系?

// 多对多关系示例
User.belongsToMany(Project, { through: 'UserProjects' });
Project.belongsToMany(User, { through: 'UserProjects' });

// 包含额外属性的关联表
const UserProjects = sequelize.define('UserProjects', {
  role: Sequelize.STRING
});
User.belongsToMany(Project, { through: UserProjects });
Project.belongsToMany(User, { through: UserProjects });

总结

通过本文,我们深入探讨了 Sequelize 和 Sequelize-CLI 的完整工作流程。关键要点:

  1. 理解工具定位:Sequelize 用于运行时操作,Sequelize-CLI 用于开发时管理
  2. 遵循迁移优先:所有数据库变更都应通过迁移文件管理
  3. 保持代码一致:模型定义与数据库结构要保持同步
  4. 重视数据安全:使用种子数据管理测试和初始数据

掌握这些概念和实践,你将能够构建健壮、可维护的 Node.js 数据库应用,轻松应对需求变更和团队协作挑战。

示例代码

下载 https://gitee.com/ericluo1008/express-sequelize-demo


吾问启玄关、AI理顺万绪!

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐