Sequelize 详细指南

Sequelize 详细指南

最近在工作中遇到了使用JS去完成service层的业务逻辑,以及数据库操作,现在下来补课

一、什么是 Sequelize?

Sequelize 是一个基于 Node.js 的 ORM(对象关系映射)工具,用于在 Node.js 环境中操作关系型数据库。它支持多种数据库系统,包括:

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

二、核心特性

1. 多数据库支持

javascript 复制代码
const { Sequelize } = require('sequelize');

// 连接不同数据库
const sequelize = new Sequelize('database', 'username', 'password', {
  dialect: 'mysql', // 或 'postgres', 'sqlite', 'mssql'
  host: 'localhost',
  port: 3306
});

2. 模型定义

javascript 复制代码
const { DataTypes } = require('sequelize');

const User = sequelize.define('User', {
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  username: {
    type: DataTypes.STRING(50),
    allowNull: false,
    unique: true
  },
  email: {
    type: DataTypes.STRING,
    validate: {
      isEmail: true
    }
  },
  age: {
    type: DataTypes.INTEGER,
    defaultValue: 18
  },
  isActive: {
    type: DataTypes.BOOLEAN,
    defaultValue: true
  }
}, {
  tableName: 'users', // 自定义表名
  timestamps: true,   // 自动添加 createdAt 和 updatedAt
  paranoid: true      // 软删除(添加 deletedAt)
});

其中定义模型时还有很多其他属性,可以自行查阅

3. 关联关系

javascript 复制代码
// 一对一
User.hasOne(Profile);
Profile.belongsTo(User);

// 一对多
User.hasMany(Post);
Post.belongsTo(User);

// 多对多
User.belongsToMany(Role, { through: 'UserRoles' });
Role.belongsToMany(User, { through: 'UserRoles' });

其中定义关联关系时,可以根据表与表之间的外键进行关联

3. 关联关系

javascript 复制代码
// 一对一
Profile.belongsTo(User, {
  foreignKey: 'userId', // Profile 表中的外键字段名
  targetKey: 'id'       // 引用 User 表的哪个字段(默认是主键)
});

// 还有很多其他属性

三、基本使用

1. 安装与配置

bash 复制代码
npm install sequelize
# 安装对应的数据库驱动
npm install pg pg-hstore       # PostgreSQL
npm install mysql2             # MySQL
npm install mariadb            # MariaDB
npm install sqlite3            # SQLite
npm install tedious            # SQL Server

2. 连接数据库

javascript 复制代码
const { Sequelize } = require('sequelize');

const sequelize = new Sequelize({
  database: 'my_database',
  username: 'root',
  password: 'password',
  host: 'localhost',
  dialect: 'mysql',
  
  // 连接池配置
  pool: {
    max: 5,
    min: 0,
    acquire: 30000,
    idle: 10000
  },
  
  // 日志配置
  logging: console.log, // 或 false 禁用日志
  
  // 时区配置
  timezone: '+08:00'
});

3. 测试连接

javascript 复制代码
async function testConnection() {
  try {
    await sequelize.authenticate();
    console.log('连接成功!');
    
    // 同步模型到数据库
    await sequelize.sync({ force: false }); // force: true 会删除现有表
  } catch (error) {
    console.error('连接失败:', error);
  }
}

四、CRUD 操作

1. 创建记录

javascript 复制代码
// 方法一:使用 create
const user = await User.create({
  username: 'john_doe',
  email: 'john@example.com',
  age: 25
});

// 方法二:先构建后保存
const user = User.build({
  username: 'jane_doe'
});
await user.save();

// 批量创建
const users = await User.bulkCreate([
  { username: 'user1' },
  { username: 'user2' }
]);

2. 查询记录

javascript 复制代码
// 查询所有
const users = await User.findAll();

// 条件查询
const user = await User.findOne({
  where: {
    username: 'john_doe',
    isActive: true
  }
});

// 使用运算符
const users = await User.findAll({
  where: {
    age: {
      [Op.gt]: 18,      // 大于
      [Op.lte]: 65      // 小于等于
    },
    username: {
      [Op.like]: '%john%' // 模糊查询
    }
  }
});

// 排序和分页
const users = await User.findAll({
  order: [['createdAt', 'DESC']],
  limit: 10,
  offset: 20
});

// 选择特定字段
const users = await User.findAll({
  attributes: ['id', 'username', 'email']
});

3. 更新记录

javascript 复制代码
// 方法一:先查询后更新
const user = await User.findByPk(1);
if (user) {
  user.username = 'new_name';
  await user.save();
}

// 方法二:直接更新
await User.update(
  { username: 'new_name' },
  { where: { id: 1 } }
);

// 更新多个
await User.update(
  { isActive: false },
  { where: { age: { [Op.lt]: 18 } } }
);

4. 删除记录

javascript 复制代码
// 软删除(如果启用了 paranoid)
await User.destroy({
  where: { id: 1 }
});

// 强制删除(即使启用了 paranoid)
await User.destroy({
  where: { id: 1 },
  force: true
});

// 删除多个
await User.destroy({
  where: { isActive: false }
});

五、高级查询

1. 关联查询

javascript 复制代码
// 包含关联数据
const userWithPosts = await User.findByPk(1, {
  include: [{
    model: Post,
    include: [Comment] // 嵌套包含
  }]
});

// 过滤关联数据
const user = await User.findByPk(1, {
  include: [{
    model: Post,
    where: { published: true },
    required: false // LEFT JOIN
  }]
});

2. 聚合查询

javascript 复制代码
// 计数
const count = await User.count({
  where: { isActive: true }
});

// 求和、平均值等
const result = await User.findAll({
  attributes: [
    'department',
    [sequelize.fn('COUNT', sequelize.col('id')), 'count'],
    [sequelize.fn('AVG', sequelize.col('salary')), 'avg_salary']
  ],
  group: ['department']
});

3. 事务处理

javascript 复制代码
const transaction = await sequelize.transaction();

try {
  // 在事务中执行操作
  await User.create({
    username: 'alice'
  }, { transaction });

  await Profile.create({
    userId: user.id,
    bio: 'Hello world'
  }, { transaction });

  // 提交事务
  await transaction.commit();
} catch (error) {
  // 回滚事务
  await transaction.rollback();
}

六、钩子(Hooks)

javascript 复制代码
User.beforeCreate(async (user, options) => {
  user.username = user.username.toLowerCase();
});

User.afterCreate(async (user, options) => {
  console.log(`用户 ${user.username} 已创建`);
});

User.beforeUpdate(async (user, options) => {
  user.updatedAt = new Date();
});

User.beforeDestroy(async (user, options) => {
  // 删除前的清理操作
});

七、数据验证

javascript 复制代码
const User = sequelize.define('User', {
  email: {
    type: DataTypes.STRING,
    validate: {
      isEmail: true,
      isLowercase: true,
      len: [5, 100]
    }
  },
  age: {
    type: DataTypes.INTEGER,
    validate: {
      min: 0,
      max: 150,
      isEven(value) {
        if (value % 2 !== 0) {
          throw new Error('年龄必须是偶数');
        }
      }
    }
  }
});

// 自定义验证消息
validate: {
  isEmail: {
    msg: '请输入有效的邮箱地址'
  }
}

八、迁移与种子数据

1. 使用 Sequelize CLI

bash 复制代码
npm install --save-dev sequelize-cli
npx sequelize-cli init

2. 创建迁移文件

bash 复制代码
npx sequelize-cli migration:generate --name create-users-table

3. 迁移文件示例

javascript 复制代码
module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable('Users', {
      id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
      },
      username: {
        type: Sequelize.STRING,
        allowNull: false
      },
      createdAt: {
        type: Sequelize.DATE,
        allowNull: false
      },
      updatedAt: {
        type: Sequelize.DATE,
        allowNull: false
      }
    });
  },

  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable('Users');
  }
};

九、性能优化

1. 连接池配置

javascript 复制代码
const sequelize = new Sequelize({
  // ... 其他配置
  pool: {
    max: 20,        // 最大连接数
    min: 5,         // 最小连接数
    acquire: 30000, // 获取连接的超时时间
    idle: 10000     // 连接空闲时间
  }
});

2. 查询优化

javascript 复制代码
// 只选择需要的字段
await User.findAll({
  attributes: ['id', 'username']
});

// 使用原生查询处理复杂查询
const results = await sequelize.query(
  'SELECT * FROM users WHERE age > :age',
  {
    replacements: { age: 18 },
    type: QueryTypes.SELECT
  }
);

3. 索引优化

javascript 复制代码
User.init({
  // ... 字段定义
}, {
  sequelize,
  indexes: [
    {
      unique: true,
      fields: ['email']
    },
    {
      fields: ['createdAt']
    }
  ]
});

十、最佳实践

  1. 模型组织:将模型定义放在单独的文件中
  2. 错误处理:统一处理数据库错误
  3. 环境配置:使用不同环境的配置
  4. 定期维护:清理旧数据,优化索引
  5. 监控:监控查询性能和连接状态

十一、常见问题

1. N+1 查询问题

javascript 复制代码
// 不好的做法:会导致 N+1 查询
const users = await User.findAll();
for (const user of users) {
  const posts = await user.getPosts(); // 每次循环都查询数据库
}

// 好的做法:使用预加载
const users = await User.findAll({
  include: [Post]
});

2. 数据类型映射

javascript 复制代码
// Sequelize 数据类型对应关系
DataTypes.STRING      // VARCHAR(255)
DataTypes.STRING(50)  // VARCHAR(50)
DataTypes.TEXT        // TEXT
DataTypes.INTEGER     // INTEGER
DataTypes.BIGINT      // BIGINT
DataTypes.FLOAT       // FLOAT
DataTypes.DOUBLE      // DOUBLE
DataTypes.DECIMAL     // DECIMAL
DataTypes.DATE        // DATETIME
DataTypes.BOOLEAN     // TINYINT(1)
DataTypes.JSON        // JSON (仅限 MySQL 5.7+、PostgreSQL、SQLite)

总结

Sequelize 是一个功能强大的 ORM 工具,它提供了:

  • 简洁的 API 进行数据库操作
  • 强大的关联关系支持
  • 数据验证和钩子机制
  • 事务支持和迁移功能
  • 多数据库兼容性

通过合理使用 Sequelize,可以大大提高 Node.js 应用中数据库操作的开发效率和代码质量。

相关推荐
用户600071819101 小时前
【翻译】我们如何打造v0版iOS应用
前端
编程猪猪侠1 小时前
打造高灵活度动态表单:基于 React + Ant Design 的 useDynamicForm hooks 实现思路
前端·react.js·前端框架
用户294655509191 小时前
游戏开发中的向量魔法
后端
兔子零10241 小时前
nginx 配置长跑(上):从一份 server 到看懂整套路由规则
后端·nginx
啥都学点的程序员1 小时前
python项目调用shardingsphere时,多进程情况下,shardingsphere配置的连接数会乘以进程数
后端
阿民不加班1 小时前
【React】使用browser-image-compression在上传前压缩图片、react上传图片压缩
前端·javascript·react.js
guchen661 小时前
C# 闭包捕获变量的经典问题分析
后端
Lear1 小时前
Lombok全面解析:极致简化Java开发的神兵利器
后端
小周在成长1 小时前
Java 单例设计模式(Singleton Pattern)指南
后端