告别复杂SQL:Egg.js + Sequelize 零基础ORM实战指南
你是否还在为手写SQL语句繁琐易错而烦恼?是否在多表关联查询中迷失方向?本文将带你从零开始,通过Egg.js框架集成Sequelize(对象关系映射,Object-Relational Mapping),用面向对象的方式操作数据库,彻底摆脱SQL依赖。读完本文你将掌握:环境配置、模型定义、CRUD操作、事务管理四大核心技能,让数据库操作像操作JavaScript对象一样简单。
框架架构概览
Egg.js作为企业级Node.js框架,采用分层架构设计,Sequelize作为ORM层衔接Service与数据库,形成清晰的数据处理流程。
Egg.js框架分层架构,ORM层位于Service与数据库之间(项目路径:gh_mirrors/egg11/egg)
环境准备与安装
项目初始化
确保已安装Node.js(v14+)和npm,通过Egg.js脚手架创建项目:
mkdir egg-sequelize-demo && cd egg-sequelize-demo
npm init egg --type=simple
npm install
安装核心依赖
安装Sequelize相关插件:
npm install egg-sequelize --save
npm install mysql2 --save # MySQL驱动
插件配置
在config/plugin.js中启用egg-sequelize插件:
// config/plugin.js
exports.sequelize = {
enable: true,
package: 'egg-sequelize',
};
数据库配置(config/config.default.js):
// config/config.default.js
exports.sequelize = {
dialect: 'mysql',
host: 'localhost',
port: 3306,
database: 'egg-sequelize-demo',
username: 'root',
password: 'your_password',
define: {
timestamps: true, // 自动添加 createdAt/updatedAt 字段
deletedAt: true, // 软删除,添加 deletedAt 字段
underscored: true // 字段驼峰转下划线
}
};
数据模型定义
模型文件结构
Egg.js推荐将模型文件放在app/model目录下,每个文件对应一个数据表:
app/
└── model/
├── user.js # 用户模型
└── post.js # 文章模型
定义用户模型
创建app/model/user.js:
// app/model/user.js
module.exports = app => {
const { STRING, INTEGER, DATE } = app.Sequelize;
const User = app.model.define('user', {
id: { type: INTEGER, primaryKey: true, autoIncrement: true },
username: { type: STRING(30), allowNull: false, unique: true },
email: { type: STRING(100), validate: { isEmail: true } },
age: { type: INTEGER, defaultValue: 0 },
created_at: DATE,
updated_at: DATE,
deleted_at: DATE
});
return User;
};
模型关联示例
创建文章模型app/model/post.js并关联用户:
// app/model/post.js
module.exports = app => {
const { STRING, TEXT, INTEGER, DATE } = app.Sequelize;
const Post = app.model.define('post', {
id: { type: INTEGER, primaryKey: true, autoIncrement: true },
title: { type: STRING(200), allowNull: false },
content: TEXT,
user_id: {
type: INTEGER,
references: {
model: 'user', // 关联用户表
key: 'id'
}
}
});
// 定义关联关系:一篇文章属于一个用户
Post.belongsTo(app.model.User, { foreignKey: 'user_id', as: 'author' });
return Post;
};
CRUD操作实战
Service层封装
按照Egg.js最佳实践,数据库操作应封装在Service层:
// app/service/user.js
const Service = require('egg').Service;
class UserService extends Service {
async create(user) {
// 创建用户
return this.ctx.model.User.create(user);
}
async findByPk(id) {
// 根据主键查询
return this.ctx.model.User.findByPk(id);
}
async findAll() {
// 查询所有用户(支持分页)
const { page = 1, limit = 10 } = this.ctx.query;
const offset = (page - 1) * limit;
return this.ctx.model.User.findAndCountAll({
limit,
offset,
attributes: { exclude: ['password'] } // 排除敏感字段
});
}
async update(id, updates) {
// 更新用户
const user = await this.ctx.model.User.findByPk(id);
if (!user) {
this.ctx.throw(404, '用户不存在');
}
return user.update(updates);
}
async destroy(id) {
// 软删除用户
const user = await this.ctx.model.User.findByPk(id);
if (!user) {
this.ctx.throw(404, '用户不存在');
}
return user.destroy();
}
}
module.exports = UserService;
Controller调用示例
// app/controller/user.js
const Controller = require('egg').Controller;
class UserController extends Controller {
async create() {
const { ctx } = this;
const user = await ctx.service.user.create(ctx.request.body);
ctx.status = 201;
ctx.body = user;
}
async show() {
const { ctx } = this;
const user = await ctx.service.user.findByPk(ctx.params.id);
ctx.body = user;
}
}
module.exports = UserController;
路由配置
// app/router.js
module.exports = app => {
const { router, controller } = app;
router.resources('users', '/api/users', controller.user);
};
高级查询与关联
多表关联查询
查询文章及其作者信息:
// app/service/post.js
async findWithAuthor(id) {
return this.ctx.model.Post.findByPk(id, {
include: [{
model: this.ctx.model.User,
as: 'author',
attributes: ['id', 'username', 'email']
}]
});
}
复杂条件查询
async findAdvanced() {
return this.ctx.model.User.findAndCountAll({
where: {
age: { [this.app.Sequelize.Op.gt]: 18 }, // 年龄大于18
createdAt: {
[this.app.Sequelize.Op.between]: [
new Date('2023-01-01'),
new Date('2023-12-31')
]
}
},
order: [['createdAt', 'DESC']],
limit: 10
});
}
事务管理
Sequelize提供两种事务管理方式,推荐使用自动事务作用域:
// app/service/order.js
async createOrder(data) {
const { ctx } = this;
// 使用事务作用域自动管理事务
return ctx.model.transaction(async t => {
// 创建订单
const order = await ctx.model.Order.create(data, { transaction: t });
// 扣减库存
await ctx.model.Product.decrement(
{ stock: data.quantity },
{ where: { id: data.productId }, transaction: t }
);
return order;
});
}
迁移与种子文件
创建迁移文件
npx sequelize-cli migration:generate --name=init-users
编辑迁移文件:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
username: {
type: Sequelize.STRING(30),
allowNull: false,
unique: true
},
// 其他字段...
});
},
down: async (queryInterface) => {
await queryInterface.dropTable('users');
}
};
执行迁移:
npx sequelize-cli db:migrate
常见问题与最佳实践
性能优化
- 按需加载字段:
attributes: ['id', 'name'] - 使用索引:在频繁查询的字段上添加索引
- 批量操作:使用
bulkCreate和bulkUpdate
错误处理
try {
await this.ctx.model.User.create(userData);
} catch (error) {
if (error.name === 'SequelizeUniqueConstraintError') {
this.ctx.throw(400, '用户名已存在');
}
throw error;
}
学习资源与进阶
- 官方文档:Sequelize中文文档
- 项目示例:egg-sequelize-example
- 扩展阅读:Egg.js Service文档
通过本文的指南,你已掌握Egg.js集成Sequelize的核心技能。ORM不仅简化了数据库操作,更提供了数据验证、关联查询等高级特性,让你专注于业务逻辑而非SQL语法。开始在你的项目中实践这些知识,体验现代化数据库操作的便捷与高效!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




