最近开始接触数据库,现在普遍用的都是Mysql数据库,简单的了解了一下sql语句,没有太深入的学习,然后就开始找相关的ORM框架,然后锁定了Sequelize,个人感觉很强大,搜索了一些文档,但是很让人费解,讲的每一部分都是那么的官方,不太容易理解,记录一下学习路程。本文档以koa+Sequelize进行编码测试。

准备工作

在尝试使用Sequelize之前先确认是否安装了Mysql数据库,安装node,这里使用的Mysql 5.6版本。

首先要创建一个项目执行命令如下:

mkdir 文件夹名称
cd 文件夹名称
npm init -y // 直接略过所有问答,全部采用默认答案

在开始之前首先要安装相关依赖:

//  安装 sequelize koa mysql2
npm install --save-dev sequelize koa mysql2

注意:这里是mysql2

建立连接

创建main.js文件,分别引入koasequelize建立与mqsql数据库连接。在实例化Sequelize时需要配置一些参数,第一个接收的参数时数据库的名称,第二个是用户名,第三个是密码,第四项是连接mysql的相关配置。

const Koa = require("koa");
const Sequelize = require("sequelize");
const app = new Koa();
const mysqlConfig ={
host: 'localhost', // 接数据库的主机
port: '3306', // 接数据库的端口
protocol: 'tcp', // 连接数据库使用的协议
dialect: 'mysql', // 使用mysql
pool: {
max: 5, // 最大连接数量
min: 0, // 最小连接数量
idle: 10000 // 连接空置时间(毫秒),超时后将释放连接
},
retry: { // 设置自动查询时的重试标志
max: 3 // 设置重试次数
},
omitNull: false, // null 是否通过SQL语句查询
timezone: '+08:00' // 解决时差 - 默认存储时间存在8小时误差
};
const sequelize = new Sequelize('aarontest', 'root', '123456',mysqlConfig );
app.listen(3000);

通过上述代码就已经与Mysql建立了连接。这个地方没有什么可以讲的,按照上述进行配置就可以了。test数据库不一定要存在不存在也是可以正常建立连接的。配置参数还有很多,这里只说了一些常用的,就不多赘述了。如果觉得上述方法比较麻烦可以通过也提供了其他方法进行连接:

const sequelize = new Sequelize('mysql://root:123456@localhost:3306/aarontest', {
...mysqlConfig
});

这种连接方式与mongoose连接方法差不多,以至于具体使用哪种方式还是要根据个人的编码习惯来决定。

建立model

sequelize是通过define方法建立模型的,Model相当于数据库中的表,该对象不能通过构造函数实例化,而只能通过sequelize.define()sequelize.import()方法创建。

const AaronTest = sequelize.define('project', {
title: Sequelize.STRING,
description: Sequelize.TEXT
})

通过上面的方法创建好model之后,使用Navicat(数据库可视化工具)仍然无法看到我们所创建的表,尴尬。。。sequelize提供了一个sync()方法可以同步模型到数据库。使用该方法的时候一定要注意所连接的数据库一定要存在否则会报错。

define方法接收三个参数,第一个参数为表名称,第二个为所需要创建的数据库字段,第三个参数是相关表配置。

const AaronTest = sequelize.define('project', {
title: Sequelize.STRING,
description: Sequelize.TEXT
});
AaronTest.sync();

这样model所创建的字段就被同步到了数据库中,同样相对应的表也被创建好了,需要注意通过这种方式同步的表会在表名称后面添加一个s作为复数。同步数据库是会默认添加两个字段createdAtupdatedAt有的时候可能不需要这两个字段。这个时候需要在第三个字段中添加timestampsfalse默认为true

const AaronTest = sequelize.define('project', {
title: Sequelize.STRING,
description: Sequelize.TEXT
},{
timestamps: false
})

上面可以看出通过Sequelize.STRING设置当前字段的类型,Sequelize提供了很多数据类型供我们进行使用:

类型 说明
STRING 将字段指定为变长字符串类型,默认长度为 255。Sequelize.STRING(64)
CHAR 将字段指定为定长字符串类型,默认长度为 255。Sequelize.CHAR(64)
TEXT 将字段指定为(无)有限长度的文本列。可用长度:tiny, medium, long,Sequelize.TEXT('tiny')
INTEGER 32位整型,可用属性:UNSIGNED,ZEROFILL,Sequelize.INTEGER('UNSIGNED')
BOOLEAN 小数,接受一个或两个参数表示精度,Sequelize.BOOLEAN()
TIME 指定为时间类型列,Sequelize.TIME()
DATE 指定为日期时间类型列,Sequelize.DATE()
DATEONLY 指定为日期类型列,Sequelize.DATEONLY()
HSTORE 指定为键/值类型列,仅Postgres适用,Sequelize.HSTORE()
JSON 指定为JSON字符串类型列,仅Postgres适用,Sequelize.JSON()
JSONB 指定为预处理的JSON数据列,仅Postgres适用,Sequelize.JSONB()
NOW 一个表示当前时间戳的默认值,Sequelize.NOW()
UUID UUID类型列,其默认值可以为UUIDV1或UUIDV4,Sequelize.UUID()
ENUM 枚举类型,Sequelize.ENUM()
ARRAY 数组类型,仅Postgres适用,Sequelize.ARRAY()

设定model的时候在添加第三个参数可以对当前model进行二次设置,以使数据库更加强壮,常用字段如下

  • timestamps:不要添加时间戳属性 (updatedAt, createdAt)
  • paranoid:paranoid 属性只在启用 timestamps 时适用,不从数据库中删除数据,而只是增加一个 deletedAt 标识当前时间,我们常说的逻辑删除
  • underscored: 不使用驼峰式命令规则,这样会在使用下划线分隔,updatedAt的字段名会是 updated_at
  • freezeTableName:禁止修改表名. 默认情况下sequelize会自动使用传入的模型名(define的第一个参数)做为表名,如果你不想使用这种方式你需要进行以下设置
  • tableName:定义表名
  • createdAt:不想使用 createdAt
  • updatedAt:想 updatedAt 的实际名为'***'
  • deletedAt: 要将 deletedAt 设置为 destroyTime (注意要启用paranoid)

当创建model的时候可能需要有些附加属性,比如主键,自增,不能为null,默认值等等,可以在创建model的时候进行手动设置。

  • autoIncrement:是否自增
  • references:通过references选项可以创建外键
  • allowNull:设置 allowNull 选项为 false 后,会为列添加 NOT NULL 非空限制
  • defaultValue:设置默认值
  • type:字段类型
  • unique:添加唯一(unique)约束后插入重复值会报错,unique属性可以是boolean 或 string类型
  • primaryKey:设置为主键
  • comment:字段描述
  • field:指定数据库中的字段名

除了这些以外Sequelize在定义model的时候,还提供了一个validate属性,可以为添加的字段进行校验处理:

字段 说明 值类型
is 存储值必须满足正则 正则
not 除正则之外的值 布尔
isEmail 是否为邮箱 布尔
isUrl 检查Url格式 布尔
isIP 检查 IPv4 或 IPv6 格式 布尔
isIPv4 检查 IPv4 布尔
isIPv6 检查 IPv6 布尔
isAlpha 不能使用字母 布尔
isAlphanumeric 只允许字母数字字符 布尔
isNumeric 只能使用数字 布尔
isInt 只能是整数 布尔
isFloat 只能是浮点数 布尔
isDecimal 检查数字 布尔
isLowercase 检查小写字母 布尔
isUppercase 检查大写字母 布尔
notNull 不允许null 布尔
isNull 只能为null 布尔
notEmpty 不能空字符串 布尔
equals 只能使用指定值 字符串
contains 必须包含子字符串 字符串
notIn 不能是数组中的任意一个值 数组
isIn 只能是数组中的任意一个值 数组
notContains 不能包含子字符串 字符串
len 值的长度必在 2 和 10 之间 数组
isUUID 只能是UUID 数字
isDate 只能是日期字符串 布尔
isAfter 只能使用指定日期之后的时间 字符串
isBefore: 只能使用指定日期之前的时间 字符串
max 允许的最大值 数字
min 允许的最小值 数字
isArray 不能使用数组 布尔
isCreditCard 检查是有效的信用卡 布尔

除了上面的校验方法以外还提供了自定义校验方法,使用isEven去定义一个函数,其函数的第一个参数就是所存如的值:

const AaronTest = sequelize.define('project', {
title: Sequelize.STRING,
description: {
type:Sequelize.TEXT,
validate:{
isEven: function(value) {
if(parseInt(value) % 2 != 0) {
throw new Error('Only even values are allowed!')
}
}
}
}
},{
timestamps: false
})

上面说过使用sequelize.import()也可以创建model这个方法其实是模型导入,通过文件导入模型定义。检查模型是否已经定义。被导入的模型会被缓存,所以多次导入并不会重复加载,path表示要导入文件的路径,如果使用相对路径会自动转换为绝对路径。

const AaronTest = sequelize.import('../model/user.js');

user.js

module.exports = function(sequelize, DataTypes) {
return sequelize.define("project", {
name: DataTypes.STRING,
description: DataTypes.TEXT
})
}

CRUD

CRUD:是指在做计算处理时的增加(Create)、读取(Read)、更新(Update)和删除(Delete)几个单词的首字母简写。crud主要被用在描述软件系统中数据库或者持久层的基本操作功能。

创建

创建数据的方法有很多种,这里简单的介绍一些常用的:

第一种:

先创建数据实例,然后调用实例的save方法,完成数据存储。

const Aaron = AaronTest.build({
'title': `后端 | ${Math.random()}`,
'description': '技术部'
});
Aaron.save().then((result) => {
// 成功
console.log(result)
}).catch((error) => {
// 失败
console.log(error)
})

第二种:

通过静态create方法

const user = AaronTest.create({
'title': `前端 | ${Math.random()}`,
'description': '网络部'
}).then(function(result) {
// 成功
console.log(result)
}).catch(function(error) {
// 失败
console.log(error)
});

读取

通过findAll方法读取数据。

AaronTest.findAll({
where: {
description: '网络部'
},
limit: 10,
offset: 0
}).then(function(result) {
// success
console.log(result)
}).catch(function(error) {
// error
console.log(error)
});

通过上述方法创建的数据可以直接作为返回结果返回给前台,但是如果对查询到的结果进行数据操作时不行的,因为查询到的结果是sequelize处理过的模型,如果想对其操作需要在参数中添加row:true属性。

AaronTest.findAll({
where: {
description: '网络部'
},
limit: 10, // 查询多少条
offset: 0, // 查询开始位置
raw:true
}).then(function(result) {
// success
console.log(result)
}).catch(function(error) {
// error
console.log(error)
});

添加row:true返回的则是一个没有被包装过的数组了。在项目过程中需要查询一下当前所查询的数据共有多少条返回给前端。

AaronTest.count({
where:{
description: '网络部'
}
}).then().then(function(result) {
// success
console.log(result)
}).catch(function(error) {
// error
console.log(error)
});

使用这种方法是确实可以查询到想要的结果,但是无论是先查询列表还是先查询总数,都需要对数据库进行两次查询很繁琐。sequelize提供了一个很方便的方法。

AaronTest.findAndCountAll({
where: {
description: '网络部'
},
limit: 10,
offset: 0,
raw:true,
attributes:["id", "title"] // 需要查询出的字段
}).then(function(result) {
// success
console.log(result)
}).catch(function(error) {
// error
console.log(error)
});

通过上面的方法则可以查询到总数以及条件范围内的数据,一举两得。查询结果返回的是一个json对象,其包括controws两个属性,分别是总数和数据。很舒服有没有。

以上方式是查询列表,查询单条数据使用其他方法:

AaronTest.findOne({
where:{
id:6
},
raw:true,
attributes:["id", "title"]
}).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})

更新

修改数据可以直接调用静态的update方法,通过where条件查询,对其搜索到的数据进行查询,并对查询到的数据进行更改。

AaronTest.update({
description: '前端部',
title:`前端 | ${Math.random()}`
},{
where:{
description: "网络部"
}
}).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})

该方法修改所有查询的到的数据,返回结果为数组形式,数据只有一个值,也就是数组的第0项,则是N条数据修改成功。

删除

删除操作通过destroy方法,同样也是通过where条件查询,对所查询数据进行删除。

AaronTest.destroy({
where: {
description: "UI部",
}
}).then(function(result) {
console.log(result)
}).catch(function(error) {
console.log(error)
});

当删除成功后,返回结果为Number,删除多少条数据,如果没有删除则会返回0。此方法属于物理删除,删除后无法进行恢复。

查询参数

CRUD操作过程中,都少不了的就是查询,细心的应该可以看的出,上面的例子中查询的时候多多少少的对其进行了一些小的改动。Sequelize中有两种查询:使用Model(模型)中的方法查询和使用sequelize.query()进行基于SQL语句的原始查询。上面用到的是Model查询方式,接下来就详细的介绍一些常用的参数以及其代表的意义。

attributes - 属性与查询字段

查询时,如果只需要查询模型的部分属性,可以在通过在查询选项中指定attributes实现。该选项是一个数组参数,在数组中指定要查询的属性即可,这个字段在上面进行查询的时候已经使用过了。

AaronTest.findOne({
where:{
id:6
},
raw:true,
attributes:["id", "title", "description"]
}).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})

查询属性(字段)可以通过传入一个嵌套数据进行重命名,这里需要强调一下重命名所指的是对查询出的数据键值进行重命名处理,而不是更改数据表中的字段名称。

AaronTest.findOne({
where:{
id:2
},
attributes:["id", ["title","t"]],
raw:true
}).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})
// 注意这里t ↓
// { id: 2, t: '前端 | 0.8765218593370694' }

通过sequelize.fn方法可以进行聚合查询,个人觉得这个方法不太常用,但是还是简单的介绍一下,这种查询方式是向当前查询内容中添加一个新的属性,并且查询的是列表还是查询单条数据。

应用场景:

比如有很多商品,每个商品都有自己的分类,根据id进行查询了一个商品,但是与其同类的商品有多少?就可以使用这个方法添加进去。下面例子中的count则是添加进去属性的键。

AaronTest.findAll({
where:{
id:2
},
attributes: [
"id",
["title","t"],
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
raw:true
}).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})

可能有一种情况,当前所需要查询的表字段太多,但是只有一两个数据不想要,在attributes数组中添加很长的字段名称,这样会显得代码很臃肿。attributes不光可以为数组,还可以为对象在对象存在exclude这个属性,这个属性就是剔除掉那些不想要的属性。

AaronTest.findOne({
where:{
id:2
},
attributes:{
exclude: ['id']
},
raw:true
}).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})

where - 指定筛选条件

上面的那么多例子中where出现的次数最多了,除了增加数据不需要,其他的都需要用到where条件,可以指定一个where选项以指定筛选条件,where是一个包含属性/值对对象,sequelize会根据此对象生产查询语句的筛选条件。

where的基础用法也就向上面那样,针对某些特定的条件进行查询处理。

AaronTest.findOne({
where:{
id:2
},
attributes:{
exclude: ['id']
},
raw:true
}).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})

就像上面那样简单的查询无法满足所有的业务需求,Sequelize还提供了操作符以满足更多的查询条件,常用的操作符如下:

$and: {a: 5}                    // AND (a = 5)
$or: [{a: 5}, {a: 6}] // (a = 5 OR a = 6)
$gt: 6, // > 6
$gte: 6, // >= 6
$lt: 10, // < 10
$lte: 10, // <= 10
$ne: 20, // != 20
$not: true, // IS NOT TRUE
$between: [6, 10], // BETWEEN 6 AND 10
$notBetween: [11, 15], // NOT BETWEEN 11 AND 15
$in: [1, 2], // IN [1, 2]
$notIn: [1, 2], // NOT IN [1, 2]
$like: '%hat', // LIKE '%hat'
$notLike: '%hat' // NOT LIKE '%hat'
$iLike: '%hat' // 包含'%hat' (case insensitive) (PG only)
$notILike: '%hat' // 不包含'%hat' (PG only)
$like: { $any: ['cat', 'hat']} // 像任何数组['cat', 'hat'] -也适用于iLike和notLike

limit/offset - 分页与限制返回结果数

在进行列表查询时,不能把查询道德所有数据全部返回出去,需要对数据进行分页处理。

// 获取 10 条数据(实例)
AaronTest.findAll({ limit: 10 })
// 跳过 8 条数据(实例)
AaronTest.findAll({ offset: 8 })
// 跳过 5 条数据并获取其后的 5 条数据(实例)
AaronTest.findAll({ offset: 5, limit: 5 })

查询排序

order选项用于查询结果的排序数据。排序时应该传入一个包含属性-排序方向的元组/数组,以保证正确的转义:

AaronTest.findAll({
order: [
// 转义 username 并对查询结果按 DESC 方向排序
['username', 'DESC'],
// 按 max(age) 排序
sequelize.fn('max', sequelize.col('age')),
// 按 max(age) DESC 排序
[sequelize.fn('max', sequelize.col('age')), 'DESC'],
// 按 otherfunction(`col1`, 12, 'lalala') DESC 排序
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
// 按相关联的User 模型的 name 属性排序
[User, 'name', 'DESC'],
// 按相关联的User 模型的 name 属性排序并将模型起别名为 Friend
[{model: User, as: 'Friend'}, 'name', 'DESC'],
// 按相关联的User 模型的嵌套关联的 Company 模型的 name 属性排序
[User, Company, 'name', 'DESC'],
]
// 以下所有声明方式都会视为字面量,应该小心使用
order: 'convert(user_name using gbk)'
order: 'username DESC'
order: sequelize.literal('convert(user_name using gbk)')
})

上面说的这些对于SQL语句了解一些,都是很容理解,有些API不常用也就没些,详细可以查看中文文档。

SQL语句查询

原始查询中有两种替换查询参数的方法,以:开头的参数的形式替换或以不命名以?替换。在选项对象中传递参数:

  • 如果传递一个数组,? 会按数组的顺序被依次替换
  • 巢传递一个对象,:key将会用对象的键替换。如果对象中未找到指定键,则会引发异常(反之亦然)
//  这里是sequelize,并不是model
sequelize.query('SELECT * FROM projects WHERE id = ?',
{ replacements: ['active'], type: sequelize.QueryTypes.SELECT }
).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})

总结

以上是对sequelizeapi进行了整理,虽然不太全面,熟练掌握上述API可以做一个项目了,有关sequelize的更多的用法我也在继续爬坑中,可能文章中有些许错误,大家可以在下方留言,我会尽快做出改正。感谢大家的阅读。

Sequelize手记 - (一)的更多相关文章

  1. nodejs项目mysql使用sequelize支持存储emoji

    nodejs项目mysql使用sequelize支持存储emoji 本篇主要记录nodejs项目阿里云mysql如何支持存储emoji表情. 因由 最近项目遇到用户在文本输入emoji进行存储的时候导 ...

  2. Linux.NET实战手记—自己动手改泥鳅(上)

    各位读者大家好,不知各位读者有否阅读在下的前一个系列<Linux.NET 学习手记>,在前一个系列中,我们从Linux中Mono的编译安装开始,到Jexus服务器的介绍,以及如何在Linu ...

  3. Linux.NET学习手记(7)

    前一篇中,我们简单的讲述了下如何在Linux.NET中部署第一个ASP.NET MVC 5.0的程序.而目前微软已经提出OWIN并致力于发展VNext,接下来系列中,我们将会向OWIN方向转战. 早在 ...

  4. Linux.NET学习手记(8)

    上一回合中,我们讲解了Linux.NET面对OWIN需要做出的准备,以及介绍了如何将两个支持OWIN协议的框架:SignalR以及NancyFX以OwinHost的方式部署到Linux.NET当中.这 ...

  5. 关于《Linux.NET学习手记(8)》的补充说明

    早前的一两天<Linux.NET学习手记(8)>发布了,这一篇主要是讲述OWIN框架与OwinHost之间如何根据OWIN协议进行通信构成一套完整的系统.文中我们还直接学习如何直接操作OW ...

  6. U3D DrawCall优化手记

    在最近,使用U3D开发的游戏核心部分功能即将完成,中间由于各种历史原因,导致项目存在比较大的问题,这些问题在最后,恐怕只能通过一次彻底的重构来解决 现在的游戏跑起来会有接近130-170个左右的Dra ...

  7. 在node.js中,使用基于ORM架构的Sequelize,操作mysql数据库之增删改查

    Sequelize是一个基于promise的关系型数据库ORM框架,这个库完全采用JavaScript开发并且能够用在Node.JS环境中,易于使用,支持多SQL方言(dialect),.它当前支持M ...

  8. 信息系统实践手记5-CACHE设计一例

    说明:信息系统实践手记系列是系笔者在平时研发中先后遇到的大小的问题,也许朴实和细微,但往往却是经常遇到的问题.笔者对其中比较典型的加以收集,描述,归纳和分享. 摘要:此文描述了笔者接触过的部分信息系统 ...

  9. 信息系统实践手记6-JS调用Flex的性能问题一例

    说明:信息系统实践手记系列是系笔者在平时研发中先后遇到的大小的问题,也许朴实和细微,但往往却是经常遇到的问题.笔者对其中比较典型的加以收集,描述,归纳和分享. 摘要:此文描述了笔者接触过的部分信息系统 ...

随机推荐

  1. [Web] mobx 异步操作

    转载自:https://www.jianshu.com/p/66dd328726d7 异步action action只能影响正在运行的函数,而无法影响当前函数调用的异步操作 .action 包装/装饰 ...

  2. thymeleaf和freemarker比较

    http://freemarker.cn/archives/168.html https://www.zhihu.com/question/64039553/answer/215942472 http ...

  3. 在 RPA10.X 运行异常,RPA9 却正常的问题处理

    一.现象 RPA10.X 在一些极少数 win7/win10 环境中一运行就崩溃或无运行结果. 二.原因 出现上述现象已经确定是OPenGL 驱动兼容性造成的. 三.解决方法 方法一 只要把流程的管理 ...

  4. 微信小程序诡异错误this.setData报错

    先说原因: function声明的函数和箭头函数的作用域不同,这是一个不小心坑的地方.可参考箭头函数说明:https://developer.mozilla.org/en-US/docs/Web/Ja ...

  5. golang调用c动态库

    golang调用c动态库 简介 golang调用c语言动态库,动态方式调用,可指定动态库路径,无需系统目录下 核心技术点 封装c动态库 go语言调用c代码 实例代码 封装c动态库 头文件 test_s ...

  6. sql server中raiserror的用法(动态参数传值)

    1.raiserrror定义: 返回用户定义的错误信息并设系统标志,记录发生错误.通过使用 RAISERROR 语句,客户端可以从 sysmessages 表中检索条目, 或者使用用户指定的严重度和状 ...

  7. elementui---表格拖动排序的问题

    刚刚用elementui的表格,需要用到一个拖动排序的需求,简单弄了下,使用 Sorttable 来做还是挺快的,但是发现一个问题,拖动排序显示不正常. <el-table :data=&quo ...

  8. Android闪屏问题的分析思路

    http://www.devba.com/index.php/archives/6157.html  Android闪屏问题的分析思路 作者:孤风一剑   发布:2015-01-22 12:35   ...

  9. C#实体类null自动转空字符串

    C#实体类null自动转空字符串 using System.ComponentModel.DataAnnotations; [DisplayFormat(ConvertEmptyStringToNul ...

  10. 【Git】Gitlab添加SSH key可以pull不能push的问题

    背景:使用webhook 钩子进行代码的自动更新 完整过程: https://zhuanlan.zhihu.com/p/93223263 问题: 在进行git pull 时候.报错了 这是gitlab ...