前言

sequelize是什么? sequelize是基于NodeJs的ORM框架,它适用于不同的数据库,如:Postgres、MySQL、SQLite、MariaDB,我们可以通过sequelize对数据库进行一系列的操作。通常我用它与MySQL一起使用。该文是我在使用sequelize做完项目后对sequelize的系统整理。

准备工作

一、创建数据库和表,方便学习过程中书写示例代码

创建数据库 lesson

CREATE DATABASE IF NOT EXISTS lesson DEFAULT CHARSET utf8 COLLATE utf8_general_ci;

创建商品表 goods(示例使用,字段从简)

CREATE TABLE IF NOT EXISTS goods(
id INT(20) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '商品id',
name VARCHAR(64) NOT NULL COMMENT '商品名称',
title VARCHAR(200) NOT NULL COMMENT '商品标题',
descript TEXT COMMENT '商品描述',
num BIGINT UNSIGNED NOT NULL COMMENT '商品库存',
cateid INT(10) UNSIGNED NOT NULL COMMENT '分类id',
price DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '商品价格',
create_time DATE COMMENT '创建时间',
update_time DATE COMMENT '修改时间'
)ENGINE=InnoDB;

创建分类表 categorys (示例使用,字段从简)

CREATE TABLE IF NOT EXISTS categorys(
id INT(20) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '分类id',
name VARCHAR(64) NOT NULL COMMENT '分类名称',
create_time DATE COMMENT '创建时间',
update_time DATE COMMENT '修改时间'
)ENGINE=InnoDB;

二、使用koa框架起一个服务

要学习使用sequelize,我们要先用node搭建基础服务,暴露接口方便测试,直观的看到代码效果。

引入包

在使用sequelize之前,我们需要引入两个包:sequelize、mysql2

npm install --save sequelize mysql2

连接数据库

要连接数据库,您必须new一个Sequelize的实例,给Sequelize构造函数传递参数完成连接。

const DB = new Sequelize(database,username,password,{
host:'',
dialect:''
})
  • database:要连接的数据库name
  • username:登录数据库时的用户名
  • password:登录数据库时的密码
  • options:选项配置参数,为一个对象,内含许多配置属性,如:
    • host:数据库主机
    • port:数据库端口号
    • dialect:指定要连接哪种类新的数据库,如:mysql、postgres等
    • define:为模型定义默认选项
    • timezone:时区

让我们先快速开始一个示例,从示例中学习需要注意的点 (请始终记住 我们使用的时准备阶段创建的数据库和表)

const {Sequelize,Model} = require('sequelize');

# 连接数据库
const connect = new Sequelize('lesson','root','abc123456',{
host:'localhost',
port:3306,
dialect:'mysql'
}) # 创建模型
class Category extends Model{} Category.init({
name:Sequelize.STRING
},{
sequelize:connect,
tableName:'categorys'
}) # 新增一点数据
Category.create({name:'房产'})

当我们运行代码新增数据时,会发现数据没有添加成功且控制台报错。

为什么会出现这个问题呢? 这是因为Sequelize为了方便开发者,它会自动帮开发者添加上创建时间和更新时间,但是它给这两个时间规定了默认字段为createdAtupdatedAt,如果不做任何配置,我们创建数据库时创建时间和修改时间字段必须与它默认规定的相同。

与这两个字段相关联的有一个timestamp配置属性,这个属性可以启用或不启用Sequelize为开发者自动添加时间,默认为true。

对上面代码做一点点更改,如下:

# 连接数据库
const connect = new Sequelize('lesson','root','abc123456',{
host:'localhost',
port:3306,
dialect:'mysql', + define:{
+ timestamp:false
+ }
})

在实例化Sequelize时,加上define参数配置,可以作用于全局。 我们如上配置全局timestamp为false,即表示关闭自动添加时间,再次运行代码 我们会发现数据库添加了一条数据。

解决了上面添加数据失败的问题,但是没有创建时间,这显然不是我们所希望的。 我们再对代码做一些改动,添加上create_time字段如下:

# 创建模型
class Category extends Model{} Category.init({
name:Sequelize.STRING,
+ update_time:{
+ type:Sequelize.DATE
+ }
},{
sequelize:connect,
tableName:'categorys'
}) # 新增一点数据
+ const create_time = new Date().getTime();
Category.create({name:'房产',create_time:create_time})

这时再打开数据库,我们可以看到有一条具有创建时间的数据已经被添加上去了

做到这里,我们知道了,不用Sequelize自动添加时间的方式,我们可以自己手动编码添加,而且数据库的时间字段可以是任意合法的字符。

当然,我们开启了timestamp,数据库的时间字段也可以是任意合法的字符。 这时候需要我们配置字段别名,这是很简单的,只需要添加两行代码就可以了,如下:

# 连接数据库
const connect = new Sequelize('lesson','root','abc123456',{
host:'localhost',
port:3306,
dialect:'mysql', + define:{
+ timestamp:true,
+ createdAt:'create_time',
+ updatedAt:'update_time'
+ }
})

上述篇幅,我们已经知道如何连接数据库和向数据库中添加数据,但是有些业务场景,不仅仅只是将我们在输入框中的数据原样插入到数据库中,可能会对输入的数据做一些改动,如用户注册时,密码需要加密然后再插入到数据库中。

需要实现这个需求,我们需要学习Sequelize的Setters、Getters和Virtuals

Setters: setter是为模型中字段的set()函数,它接收字段的值。它作用在数据的添加阶段,在set()函数中,我们可以获取字段的值并对原始值做一系列的变化,然后再设置最终值。

Getters: getter是为模型中字段的get()函数,它作用在数据的获取阶段,在get()函数中,我们可以先对返回值做一系列的操作,再返回最终值。

Virtuals: virtuals是虚拟字段,它不存在与数据库中。开发者可以使用它将两个字段拼接返回。

接下来我们来看看实际使用的栗子,代码如下:

Category.init({
name:{
type:Sequelize.STRING,
set(val){
const newVal = val + 'sb'; // 所有添加的数据 name后都加上sb字符
this.setDataValue('name',newVal);
},
get() {
const rawValue = this.getDataValue('name'); //获取字段值
return rawValue ? rawValue.toUpperCase() : null; //将字段值的字母转换成大写输出
}
},
vitName:{ // 这是一个虚拟字段
type:Sequelize.VIRTUAL,
get(){
return `${this.name}`
}
}
},{
sequelize:connect,
tableName:'categorys'
})

到现在,我们已经学习了Sequelize很多的知识点了,接下来我们继续学习Sequelize的查询、修改、删除等方法。 在学习之前 我们还是先将数据库的数据维护一份正式一点的数据。

分别是分类表和商品表 如下图:

Sequelize为开发者提供了查找方法,默认情况下,所有查找器方法的结果都是模型类的实例,而不是简单的javascript对象。这意味着在数据库返回结果后,Sequelize会自动将所有内容打包到适当的实例对象中。在某些情况下,当结果太多时,这种包装可能效率低下,可以通过{raw:true}选项配置禁用。

查询选择器:

  • findAll:它生成一条标准的SELECT查询语句,在不受到条件语句限制的情况下从表中检索所有数据。
  • findByPk:根据提供的主键,从表中检索处一条数据。
  • findOne:如果不提供查询选项的话,它会检索到表中第一条数据。
  • count:检索数据库中所有数据,得到数据总数。
  • findAndCountAll:可以获得数据总量和数据列表。这在做分页数据时时非常方便的。

现在我们来查询表数据,实现一些需求。

所有商品:
router.get('/pro/list',async (ctx,next) => {
const result = await Product.findAll();
ctx.body = {
msg:result
}
})

得到的结果为:

{
"msg": [
{
"id": 2,
"name": "工装七分裤",
"title": "优衣库 男装 工装七分裤(卷边) 425146 UNIQLO ",
"descript": "优衣库 男装 工装七分裤(卷边) 425146 UNIQLO 优衣库 男装 工装七分裤(卷边) 425146 UNIQLO ",
"num": 158,
"cateid": 2,
"price": "149.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
},
{
"id": 3,
"name": "花花公子旗舰短袖t恤",
"title": "花花公子旗舰短袖t恤男2020新款夏季潮牌宽松男士半袖潮流打底衫T ",
"descript": "花花公子旗舰短袖t恤男2020新款夏季潮牌宽松男士半袖潮流打底衫T 花花公子旗舰短袖t恤男2020新款夏季潮牌宽松男士半袖潮流打底衫T ",
"num": 3000,
"cateid": 2,
"price": "269.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
},
{
"id": 4,
"name": "华为nova",
"title": "Huawei/华为 nova6 SE超级快充4800万AI四摄大运存nova6se 华为手机华为官方旗舰店 ",
"descript": "Huawei/华为 nova6 SE超级快充4800万AI四摄大运存nova6se 华为手机华为官方旗舰Huawei/华为 nova6 SE超级快充4800万AI四摄大运存nova6se 华为手机华为官方旗舰店",
"num": 2500,
"cateid": 3,
"price": "1999.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
},
{
"id": 5,
"name": "荣耀30",
"title": "华为旗下荣耀30新品5G手机50倍超稳远摄麒麟985芯片全新智能手机正品官方旗舰店",
"descript": "华为旗下荣耀30新品5G手机50倍超稳远摄麒麟985芯片全新智能手机正品官方旗舰店华为旗下荣耀30新品5G手机50倍超稳远摄麒麟985芯片全新智能手机正品官方旗舰店",
"num": 3512,
"cateid": 3,
"price": "3299.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
},
{
"id": 6,
"name": "美的变频空调",
"title": "美的i青春大1.5匹空调智能挂机冷暖壁挂式官方旗舰店",
"descript": "美的i青春大1.5匹空调智能挂机冷暖壁挂式官方旗舰店美的i青春大1.5匹空调智能挂机冷暖壁挂式官方旗舰店",
"num": 4215,
"cateid": 4,
"price": "2999.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
},
{
"id": 7,
"name": "格力Gree",
"title": "Gree/格力 KFR-35GW 大1.5匹空调挂机智能变频冷暖一级壁挂式品悦",
"descript": "Gree/格力 KFR-35GW 大1.5匹空调挂机智能变频冷暖一级壁挂式品悦Gree/格力 KFR-35GW 大1.5匹空调挂机智能变频冷暖一级壁挂式品悦",
"num": 4215,
"cateid": 4,
"price": "4199.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
},
{
"id": 8,
"name": "郁香菲2020夏季新品",
"title": "郁香菲2020夏季新品 荷叶边两穿收腰长款连衣裙纯色垂感飘逸长裙 ",
"descript": "郁香菲2020夏季新品 荷叶边两穿收腰长款连衣裙纯色垂感飘逸长裙 郁香菲2020夏季新品 荷叶边两穿收腰长款连衣裙纯色垂感飘逸长裙 ",
"num": 4215,
"cateid": 5,
"price": "699.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
},
{
"id": 9,
"name": "美背文胸",
"title": "运动内衣女无钢圈聚拢冰丝无痕背心式胸罩夏季薄款调整型美背文胸 ",
"descript": "运动内衣女无钢圈聚拢冰丝无痕背心式胸罩夏季薄款调整型美背文胸 运动内衣女无钢圈聚拢冰丝无痕背心式胸罩夏季薄款调整型美背文胸 ",
"num": 10000,
"cateid": 5,
"price": "99.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
}
]
}
条件查询

查询

router.get('/pro/list',async (ctx,next) => {
const result = await Product.findAll({
where:{
id:4
}
});
ctx.body = {
msg:result
}
})

结果:

{
"msg": [
{
"id": 4,
"name": "华为nova",
"title": "Huawei/华为 nova6 SE超级快充4800万AI四摄大运存nova6se 华为手机华为官方旗舰店 ",
"descript": "Huawei/华为 nova6 SE超级快充4800万AI四摄大运存nova6se 华为手机华为官方旗舰Huawei/华为 nova6 SE超级快充4800万AI四摄大运存nova6se 华为手机华为官方旗舰店",
"num": 2500,
"cateid": 3,
"price": "1999.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
}
]
}

查询

router.get('/pro/list',async (ctx,next) => {
const result = await Product.findOne({
where:{
id:4
}
});
ctx.body = {
msg:result
}
})
# 等同于
router.get('/pro/list',async (ctx,next) => {
const result = await Product.findByPk(4);
ctx.body = {
msg:result
}
})

结果一样,都是

{
"msg": {
"id": 4,
"name": "华为nova",
"title": "Huawei/华为 nova6 SE超级快充4800万AI四摄大运存nova6se 华为手机华为官方旗舰店 ",
"descript": "Huawei/华为 nova6 SE超级快充4800万AI四摄大运存nova6se 华为手机华为官方旗舰Huawei/华为 nova6 SE超级快充4800万AI四摄大运存nova6se 华为手机华为官方旗舰店",
"num": 2500,
"cateid": 3,
"price": "1999.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
}
}

注意:findAll和findOne、findByPk方法得到的结果是不同的,findAll得到的结果是数组包含着数据项,而findOne、findByPk得到的结果就是一条数据

分页查询

我们在项目中经常会需要分页查询,一次性查询所有的数据,无论是服务器还是前端页面显示都是不友好的。Sequelize为开发者提供了分页机制,我们只需配置offsetlimit两个属性就可以实现分页。

  • offset:偏移量,以0开始做偏移
  • limit:限制条数

值得我们注意的是offset是以0开始做偏移的,通常前端分页会传递两个参数:pageCurrentpageSize。 pageCurrent通常是以1开始的。所以后端接收到pageCurrent后需要减1传递给Sequelize做偏移。

router.get('/pro/list',async (ctx,next) => {
const { pageCurrent,pageSize } = ctx.request.body;
let limit = pageSize ? pageSize : limit;
let offset = pageCurrent ? (pageCurrent - 1)*limit : offset;
const result = await Product.findAll({
offset,
limit
});
ctx.body = {
msg:result
}
})
指定字段
router.get('/pro/list',async (ctx,next) => {
const result = await Product.findOne({
attributes:['name','price']
});
ctx.body = {
msg:result
}
})

结果

{
"msg": {
"name": "工装七分裤",
"price": "149.00"
}
}
排除字段
router.get('/pro/list',async (ctx,next) => {
const result = await Product.findOne({
attributes:{
exclude:['descript']
}
});
ctx.body = {
msg:result
}
})

结果

{
"msg": {
"id": 2,
"name": "工装七分裤",
"title": "优衣库 男装 工装七分裤(卷边) 425146 UNIQLO ",
"num": 158,
"cateid": 2,
"price": "149.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
}
}
排序查询

配置order选项,order是一个数组,字段名和排序方式。 desc(降序),asc(升序)

router.get('/pro/list',async (ctx,next) => {
const result = await Product.findAll({
order:[
['id','desc']
]
});
ctx.body = {
msg:result
}
})
关联查询

sequelize提供了四种类型的关联关系

  • hasOne关联:与目标建立一对一关联关系,外键存在于目标模型中
  • belongsTo关联:与目标建立一对一关联关系,外键存在于源模型中
  • hasMany:与目标建立一对多关联关系,外键存在于目标模型中
  • belongsToMany:与目标建立多对多关联关系,外键存在于源模型中
# 外键存在于源模型中
Product.belongsTo(Category,{
foreignKey:'cateid',
targetKey:'id'
}) # 外键存在于目标模型中
Product.hasOne(Category,{
as:'cate',
foreignKey:'id',
targetKey:'cateid'
})

上面两个代码示例, Product模型是源模型,Category模型是目标模型。cateid是Product中定义的字段列,可以看到使用belongsTo时外键foreignKey的值是cateid 使用hasOne时外键foreignKey的值是id ,这个id是Category中的主键id。

示例代码:

Product.hasOne(Category,{
foreignKey:'id',
targetKey:'cateid'
}) router.get('/pro/list',async (ctx,next) => {
const result = await Product.findOne({
include:{
model:Category
}
});
ctx.body = {
msg:result
}
})

得到结果:

{
"msg": {
"id": 2,
"name": "工装七分裤",
"title": "优衣库 男装 工装七分裤(卷边) 425146 UNIQLO ",
"descript": "优衣库 男装 工装七分裤(卷边) 425146 UNIQLO 优衣库 男装 工装七分裤(卷边) 425146 UNIQLO ",
"num": 158,
"cateid": 2,
"price": "149.00",
"create_time": "2020-07-25",
"update_time": "2020-07-25",
"Category": {
"id": 2,
"name": "男装",
"create_time": "2020-07-25",
"update_time": "2020-07-25"
}
}
}

商品信息中就有了该商品所属分类的信息,可以看到返回的商品信息字段是Category即是以定义的类名Category为字段,我们可以更改它。

Product.hasOne(Category,{
+ as:'cate',
foreignKey:'id',
targetKey:'cateid'
}) router.get('/pro/list',async (ctx,next) => {
const result = await Product.findOne({
include:{
model:Category,
+ as:'cate'
}
});
ctx.body = {
msg:result
}
})

这样分类信息的字段就跟更改成了cate

Category.update(param,option)

  • param:修改的参数集合,Object
  • option:配置选项
router.post('/edit',async (ctx,next) => {
await Category.update({
name:'男装内裤'
},{
where:{
id:2
}
})
})

上面代码 修改id等于2的那条数据,将name字段的值更改为'男装内裤'。

Category.destroy(option)

Category.destroy({
where:{
id:2
}
})

项目总结,彻底掌握NodeJS中如何使用Sequelize的更多相关文章

  1. 在Nodejs中如何调用C#的代码

    最近需要在Nodejs中用到C#的代码,从网上了解到可以采用Edgejs来实现Nodejs与C#的代码交互, 直接复制网上的代码运行总是出各种错,填了不少坑,现在把自己的案例代码大致整理一下,方便以后 ...

  2. nodejs中常用加密算法

    在常用的nodejs+express工程中,为了安全在登录及表单传输时,应该都需进行加密传输,目前个人常用到的加密方式有下列几种: 1.Hash算法加密: 创建一个nodejs文件hash.js,输入 ...

  3. 如何优雅的处理Nodejs中的异步回调

    前言 Nodejs最大的亮点就在于事件驱动, 非阻塞I/O 模型,这使得Nodejs具有很强的并发处理能力,非常适合编写网络应用.在Nodejs中大部分的I/O操作几乎都是异步的,也就是我们处理I/O ...

  4. nodeJS中的包

    前面的话 Node组织了自身的核心模块,也使得第三方文件模块可以有序地编写和使用.但是在第三方模块中,模块与模块之间仍然是散列在各地的,相互之间不能直接引用.而在模块之外,包和NPM则是将模块联系起来 ...

  5. nodejs中http-proxy使用小结

    最近在写xmocker的工具,用于开发前期的mock数据,不可避免的用到了代理的中间件.当然,github上有关于http-proxy封装的中间件.毕竟是自己练手的项目,就自己写了个中间件,方便定制功 ...

  6. 解决nodejs中json序列化时Date类型默认为UTC格式

    在nodejs中,json序列化时Date类型时,默认转为UTC格式. 如下图 上面只是一个例子,下面我用一个更具体化的例子来展示一个这个情况,我们在开发WEB项目中,经常用到Express组件, 我 ...

  7. 解决nodejs中json序列化时Date类型为UTC格式

    在nodejs中,json序列化时Date类型时,默认转为UTC格式. 如下图 zhupengfei@DESKTOP-HJASOE3 MINGW64 /d/MyProject/exp2 $ node ...

  8. [转]nodejs中package.json和package-lock.json文件的功能分析

    本文转自:https://blog.csdn.net/u013992330/article/details/81110018 最新版nodejs中,多了一个package-lock.json文件,刚开 ...

  9. NodeJS中使用swig模板引擎

    NodeJS中的默认引擎是jade有点过于复杂,而且不是以HTML为基础的,学习成本和前端适应成本都很大.而ejs虽然简单,但不支持模板导入,而且效率一般. swig的语法简单,学习成本很低,符合常规 ...

随机推荐

  1. 打开指定大小的新窗口和window.open参数

    用法: <SCRIPT LANGUAGE="javascript">   window.open ('要打开的路径', '窗口名称', '参数列表');</SCR ...

  2. Spring Boot入门系列(十七)整合Mybatis,创建自定义mapper 实现多表关联查询!

    之前讲了Springboot整合Mybatis,介绍了如何自动生成pojo实体类.mapper类和对应的mapper.xml 文件,并实现最基本的增删改查功能.mybatis 插件自动生成的mappe ...

  3. centos6.4 卸载 vim7.2 安装vim7.4

    一.# rpm -qa|grep vim vim-minimal-7.2.-1.8.el6.x86_64 vim-enhanced-7.2.-1.8.el6.x86_64 vim-common-7.2 ...

  4. HTML文档解析和DOM树的构建

    浏览器解析HTML文档生成DOM树的过程,以下是一段HTML代码,以此为例来分析解析HTML文档的原理 <!DOCTYPE html> <html lang="en&quo ...

  5. 理解css中min-width和max-width,width与它们之间的区别联系

    css中,min-width是用来限制元素的最小宽度,max-width用来限制元素的最大宽度,也就是说当元素的width大于max-width,或者小于min-width.就被它们的值所代替,尤其适 ...

  6. MySQL实验 内连接优化order by+limit 以及添加索引再次改进

    MySQL实验 内连接优化order by+limit 以及添加索引再次改进 在进行子查询优化双参数limit时我萌生了测试更加符合实际生产需要的ORDER BY + LIMIT的想法,或许我们也可以 ...

  7. Python Java 快速配置环境变量(Path)

    Python Java 快速配置环境变量(Path) 最近系统被重置,清空了C盘中的program等文件夹以及初始化了环境变量. 通常环境下,在windows环境中我们都会打开"环境变量&q ...

  8. MVC中model、dao、view、controlller、service之间的关系

    Model:是事物的模型,如Person.java,定义人的属性行为.pojo,OR maping,持久层 Dao:是持久化操作代码编写处,与数据库对接,如对Person进行增删改查. Service ...

  9. Sightseeing,题解

    题目: 题意: 找到从s到t与最短路长度相差少于1的路径总数. 分析: 首先,搞明白题意之后,我们来考虑一下怎么处理这个1,怎样找相差为1的路径呢?我们这样想,如果有相差为1的路径,那么它将会是严格的 ...

  10. VS2017未能添加对"System.Drawing.dll"的引用

    问题: 解决方法:在程序集中找到System.Drawing.dll然后勾选引用.