Sequelize ORM sql 语句工具
初始化配置
- 在命令行中全局安装
npm i -g sequelize-cli
- sequelize 执行需要匹配 mysql2 对应的依赖(安装 mysql2)
npm i sequelize mysql2
- 初始化项目
sequelize init
- 熟悉初始化项目后的项目结构
- config:时配置的意思,这里放的也就是 sequelize 所需要的连接数据库的配置文件
- migrations:是迁移的意思,如果你需要对数据库做新增表,修改字段,删除表等操作,就需要在这里添加迁移文件了。而不是像以前那样,使用客户端软件直接操作数据库
- models:这里面存放的模型文件,当我们使用 sequelize 来执行增删改查时,就需要用这里的模型文件了,每个模型都对应数据库中的一张表。
- seeders:是存放种子文件。一般会将一些需要添加到数据表的测试数据存放在这里。只需要执行一个命令,数据表中就回自动填充进一些用来测试内容的了。
- 配置 config.js 文件
- 第一个要改的就是密码,修改成 docker 配置里,我们设定的密码。接着要改的是数据库的名字,改为 clwy_api_development。
- 最下面,还要加上时区的配置,因为我们中国是在+8 区。这样在查询的时候,时间才不会出错。
- 那么同样的,也简单的给 test 和 production 也调整一下。
注意:
json 文件中都必须为字符串
要不有可能会报错。
- The “data” argument must be one of type string, TypedArray, or DataView. Received type number (我将password配置为了 number,所以有了这个报错。改成 string 类型即可。)
{
"development": {
"username": "root",
"password": "",
"database": "yourmysql",
"host": "127.0.0.1",
"dialect": "mysql",
"timezone": "+08:00"
},
"test": {
"username": "root",
"password": null,
"database": "yourtestmysql",
"host": "127.0.0.1",
"dialect": "mysql",
"timezone": "+08:00"
},
"production": {
"username": "root",
"password": null,
"database": "yourproductionmysql",
"host": "127.0.0.1",
"dialect": "mysql",
"timezone": "+08:00"
}
}
- 使用 ORM 创建一个表
sequelize model:generate --name Article --attributes title:string,content:text
- 运行迁移
sequelize db:migrate
- 种子文件
sequelize seed:generate --name article
完成后,在 seeds 目录,就看到刚才命令新建的种子文件了。同样也是分为两个部分,up 部分用来填充数据,down 部分是反向操作,用来删除数据的。
种子文件改写代码如下
async up (queryInterface, Sequelize) {
const articles = [];
const counts = 100;
for (let i = 1; i <= counts; i++) {
const article = {
title: `文章的标题 ${i}`,
content: `文章的内容 ${i}`,
createdAt: new Date(),
updatedAt: new Date(),
};
articles.push(article);
}
await queryInterface.bulkInsert('Articles', articles, {});
},
- 运行种子
sequelize db:seed --seed xxx-article
sequelize 简化接口封装案例
- 在执行完上面操作(创建models,运行种子文件)后,进行下面接口封装
/admin/articles 文件代码
const express = require("express");
const router = express.Router();
const { Article } = require("../../models");
const { Op } = require("sequelize");
const { NotFoundError, success, failure } = require("../../utils/response.js");
/* GET home page. */
router.get("/getArticlesList", async function (req, res, next) {
try {
const query = req.query;
const currentPage = Math.abs(Number(query.currentPage) || 1);
const pageSize = Math.abs(Number(query.pageSize) || 10);
/*
变量定义
offset 从那一页开始
limit 查询多少条
currentPage 当前页码
分页查询公式
offset = (currentPage - 1) * limit
*/
const offset = (currentPage - 1) * pageSize;
const condition = {
// order: [["id", "DESC"]], // 根据id 倒序排列。数组套数组 是可以根据多个字段进行排列
limit: pageSize,
offset,
};
// 根据所传参数进行 模糊查询
if (query.title) {
condition.where = {
title: { [Op.like]: `%${query.title}%` },
};
}
// 查询所有参数
// const list = await Article.findAll(condition);
// 根据数量查询
const { count, rows } = await Article.findAndCountAll(condition);
success(res, "获取文章列表成功", {
list: rows,
pagination: { currentPage, pageSize, totalCount: count },
});
} catch (error) {
failure(res, error);
}
});
// 创建文章
router.post("/createArticle", async function (req, res, next) {
// 白名单过滤 ,避免用户乱填 id
try {
const body = {
title: req.body.title,
content: req.body.content,
};
const article = await Article.create(body);
success(res, "文章创建成功", { article }, 201);
} catch (error) {
failure(res, error);
}
});
// 删除文章
router.delete("/deleteArticle/:id", async function (req, res) {
try {
const id = req.params.id;
// 查询文章 ,看是否存在
const article = await getArticle(id);
await article.destroy(id);
success(res, "删除成功", { id }, 204);
} catch (error) {
failure(res, error);
}
});
// 更新文章
router.put("/updateArticle/:id", async function (req, res) {
try {
const id = req.params.id;
// 查询文章 ,看是否存在
const article = await getArticle(id);
// 白名单过滤 ,避免用户乱填 id
const body = {
title: req.body.title,
content: req.body.content,
};
await article.update(body);
success(res, "更新成功", { article }, 204);
} catch (error) {
failure(res, error);
}
});
// 查询数据
async function getArticle(req) {
const { id } = req.params;
const article = await Article.findByPk(id);
if (!article) {
throw new NotFoundError(`ID:${id}的文章未找到`);
}
return article; // 返回数据给中间件,供其他中间件使用。
}
module.exports = router;
/utils 文件夹
/**
* 自定义 404 错误类
*/
class NotFoundError extends Error {
constructor(message) {
super(message);
this.name = "NotFoundError";
this.status = 404;
}
}
function success(res, message, data, code = 200) {
res.status(code).json({
status: true,
message,
data,
});
}
// 报错消息封装
function failure(res, error) {
if (error.name === "SequelizeValidationError") {
const errors = error.errors.map((item) => item.message);
return res
.status(400)
.json({ status: false, message: "请求参数错误", errors });
}
if (error.name === "NotFoundError") {
return res.status(404).json({
status: false,
message: "资源不存在",
errors: [error.message],
});
}
res
.status(500)
.json({ status: false, message: "服务器错误", errors: [error.message] });
}
module.exports = {
NotFoundError,
success,
failure,
};
sequelize 验证表单
- 在 model 文件夹中找到对应的
数据表的 js 文件
- 针对需要验证的 属性,配置 validate 配置项
- 相关验证规则@sequelize 中文官网
/models/article.js 文件内容
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Article extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
Article.init(
{
title: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: {
msg: "title 不允许为空",
},
},
},
content: {
type: DataTypes.STRING,
allowNull: true,
validate: {
notEmpty: {
msg: "content 不允许为空",
},
},
},
},
{
sequelize,
modelName: "Article",
}
);
return Article;
};
sequelize 指令执行相关报错
Unable to resolve sequelize package in C:\Users\
, 无法解析 C:\Users\中的 sequelize 包. 依次执行下面指令
- npm install sequelize-cli -g
- sequelize -h ,显示 Unable to resolve sequelize package in C:\Users\
- npm install --save sequelize
sequelize 报错ERROR: Validation error
- 定义一个表时,经常会为某些字段设置唯一性约束(Unique),以确保数据的唯一性。
error: validation error
报错,某个字段可能存在重复值.- 解决办法:
- 找到设置
Unique(唯一索引)
的字段, 然后在种子文件中,对重复的数据进行整改- 参考文章
sequelize 迁移文件相关使用
迁移运行
- 当我们使用 sequelize 创建了数据表之后,会在项目里自动生成 models ,migrations 文件
- 生成上述文件后,我们还需使用文件迁移指令,才能在数据库生成上面 对应的数据表
- 执行
sequelize db:migrate
后,数据库就会生成对应的数据表
迁移回退 (注意:回退之前最好备份一下数据表)
- 当我们迁移文件后,发现部分属性不是我们想要的配置后
- 执行
sequelize db:migrate:undo
,就会撤销最近一次的迁移操作.- 在
migratios
文件中配置好相关属性后, 就可以 重新执行sequelize db:migrate
进行文件迁移
sequelize 新增表 练习
注意:
- 新增表的属性,
多个属性之间使用 逗号隔开
.
- 新增一张
Category
数据表
sequelize model:generate --name Category --attributes name:string,rank:integer
命令执行后 migrations 文件配置(category)
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Categories', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER.UNSIGNED
},
name: {
type: Sequelize.STRING,
allowNull: false,
},
rank: {
type: Sequelize.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 1
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
- 新建一张
User
用户表
sequelize model:generate --name User --attributes email:string,username:string,password:string,nick:string,sex:tinyint,company:string,introduce:text,role:tinyint
migrations 文件配置(user)
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER.UNSIGNED
},
email: {
allowNull: false,
type: Sequelize.STRING
},
username: {
allowNull: false,
type: Sequelize.STRING
},
password: {
allowNull: false,
type: Sequelize.STRING
},
nick: {
type: Sequelize.STRING
},
sex: {
allowNull: false,
type: Sequelize.TINYINT
},
company: {
type: Sequelize.STRING
},
introduce: {
type: Sequelize.TEXT
},
role: {
type: Sequelize.TINYINT
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
await queryInterface.addIndex(
"Users", {
fields: ['email'],// 需要索引的属性
unique: true,// 唯一索引
}
);
await queryInterface.addIndex(
"Users", {
fields: ['username'],// 需要索引的属性
unique: true,// 唯一索引
}
);
await queryInterface.addIndex(
"Users", {
fields: ['role'],
}
);
},
- 新增一张
Course
课程表
sequelize model:generate --name Course --attributes categoryId:integer,userId:integer,name:string,image:string,recomended:boolean,introductory:boolean,content:text,likesCount:integer,chaptersCount:integer
migrations 文件配置(Course)
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Courses', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER.UNSIGNED
},
categoryId: {
allowNull: false,
type: Sequelize.INTEGER.UNSIGNED
},
userId: {
allowNull: false,
type: Sequelize.INTEGER.UNSIGNED
},
name: {
type: Sequelize.STRING
},
image: {
type: Sequelize.STRING
},
recomended: {
type: Sequelize.BOOLEAN
},
introductory: {
type: Sequelize.BOOLEAN
},
content: {
type: Sequelize.TEXT
},
likesCount: {
allowNull: false,
type: Sequelize.INTEGER.UNSIGNED
},
chaptersCount: {
allowNull: false,
type: Sequelize.INTEGER.UNSIGNED
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
await queryInterface.addIndex(
"Course", {
fields: ['categoryId'],
}
);
await queryInterface.addIndex(
"Course", {
fields: ['userId'],
}
);
},
- 新增一张
Chapter
章节表
sequelize model:generate --name Chapter --attributes courseId:integer,title:string,content:text,video:string,rank:integer
migrations 文件配置(Chapter)
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Chapters', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER.UNSIGNED
},
courseId: {
allowNull: false,
type: Sequelize.INTEGER
},
title: {
allowNull: false,
type: Sequelize.STRING
},
content: {
type: Sequelize.TEXT
},
video: {
type: Sequelize.STRING
},
rank: {
allowNull: false,
type: Sequelize.INTEGER.UNSIGNED
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
await queryInterface.addIndex(
"Chapters", {
fields: ['courseId'],
}
);
},
- 新增一张
Like
点赞表
sequelize model:generate --name Like --attributes courseId:integer,userId:integer
migrations 文件配置 (Like)
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Likes', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER.UNSIGNED
},
courseId: {
allowNull: false,
type: Sequelize.INTEGER.UNSIGNED
},
userId: {
allowNull: false,
type: Sequelize.INTEGER.UNSIGNED
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
await queryInterface.addIndex(
"Likes", {
fields: ['courseId'],
}
);
await queryInterface.addIndex(
"Likes", {
fields: ['userId'],
}
);
},
- 新增一张
Setting
系统设置表
sequelize model:generate --name Setting --attributes name:string,icp:string,copyright:string
migrations 文件配置 (Setting)
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Settings', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER.UNSIGNED
},
name: {
type: Sequelize.STRING
},
icp: {
type: Sequelize.STRING
},
copyright: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
sequelize seed
(种子文件) 相关使用
生成种子文件
- sequelize seed:generate --name [种子文件名称]
运行种子文件
- sequelize db:seed --seed [种子文件名称]
配置数据模型 (model,表单验证)
- 找到对应的 model 模型文件,对指定属性进行表单验证
生成种子文件练习
- 生成 category 种子文件
sequelize seed:generate --name category
category 种子文件 mock 数据
async up(queryInterface, Sequelize) {
await queryInterface.bulkInsert('Categories', [
{ name: '前端开发', rank: 1, createdAt: new Date(), updatedAt: new Date() },
{ name: '后端开发', rank: 2, createdAt: new Date(), updatedAt: new Date() },
{ name: '移动端开发', rank: 3, createdAt: new Date(), updatedAt: new Date() },
{ name: '数据库', rank: 4, createdAt: new Date(), updatedAt: new Date() },
{ name: '服务器运维', rank: 1, createdAt: new Date(), updatedAt: new Date() },
{ name: '公共', rank: 6, createdAt: new Date(), updatedAt: new Date() },
])
},
sequelize 接口开发大概步骤
- 种子填充数据
- 修改模型(增加验证、增加关联)
- 复制其他路由文件,进行查找替换
- 修改白名单和搜索
- app.js 中添加路由
- Apifox 测试
sequelize 对已经生成的数据表,进行新增/修改字段
- 在我们新建的数据表应已经有大量数据,但是我们又需要对目标数据表进行 新增/修改 字段的操作.
- 可以使用
sequelize migration:create
指令,重新生成一个 迁移文件,对目标数据表进行操作
注意:
再执行了新增的迁移文件后,需要到对应的模型文件中,添加新增的 属性/字段.
add-avatar-to-user : 表示给
user
数据表添加 avatar 字段
sequelize migration:create --name add-avatar-to-user
打开add-avatar-to-user
迁移文件,配置如下
async up(queryInterface, Sequelize) {
await queryInterface.addColumn('Users', 'avatar', {
type: Sequelize.STRING
})
},
打开 user models 文件,初始化添加 avatar 字段
User.init(
{
email: DataTypes.STRING,
username: DataTypes.STRING,
password: DataTypes.STRING,
nick: DataTypes.STRING,
sex: DataTypes.TINYINT,
company: DataTypes.STRING,
introduce: DataTypes.TEXT,
role: DataTypes.TINYINT,
// 新增字段后,一定要手动配置一下 新增的字段.否则后续新增/更新操作都找不到 新增的字段.
avatar: DataTypes.STRING,
},
{
sequelize,
modelName: "User",
}
);
return User;