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. 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 应用中数据库操作的开发效率和代码质量。