sequelize学习
提示
在写 egg.js 项目时发现自己对于 sequelize 还不太熟悉,于是打算看着官方文档学习一下 Sequelize官方文档 (opens new window)
# 学习目标
学习 sequelize
主要为了使用,因此主要关注应用方面。
# 在数据库中定义模型
sequelize.define(modelName, attributes, options)
调用实例的define
方法可以将对应的模型加入到 sequelize 中,绑定为 sequelize.models[modelName]。
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');
const User = sequelize.define('User', {
// 在这里定义模型属性
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING
// allowNull 默认为 true
}
}, {
// 这是其他模型参数
});
// `sequelize.define` 会返回模型
console.log(User === sequelize.models.User); // true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 首先继承
Model
,并调用父类的init
方法来定义
const { Sequelize, DataTypes, Model } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');
class User extends Model {}
User.init({
// 在这里定义模型属性
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING
// allowNull 默认为 true
}
}, {
// 这是其他模型参数
sequelize, // 我们需要传递连接实例
modelName: 'User' // 我们需要选择模型名称
});
// 定义的模型是类本身
console.log(User === sequelize.models.User); // true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 两种方法实际上是共用一个方法,是等效的
# 根据模型创建实例,向表中加入新数据
- 完整流程
build
+save
build
是少数非异步的方法,大部分方法都是异步的
const jane = User.build({ name: "Jane" });
console.log(jane instanceof User); // true
console.log(jane.name); // "Jane"
await jane.save();
console.log('Jane 已保存到数据库!');
2
3
4
5
6
- 快捷流程
create
const jane = await User.create({ name: "Jane" });
// Jane 现在存在于数据库中!
console.log(jane instanceof User); // true
console.log(jane.name); // "Jane"
2
3
4
- 更新实例:create后使用set更新实例,之后进行保存
- 删除实例:create后使用destroy销毁实例
- 重载实例:create后使用reload可以获取数据库中的最新记录
- 保存实例:保存时可以指定fields来指定仅保留部分字段
- 使用
increment
和decrement
方法可以解决递增和递减时的并发问题
# 基础查询
# INSERT
- 使用
create
方法即可插入新数据
const user = await User.create({
username: 'alice123',
isAdmin: true
}, { fields: ['username'] }); // 在这里可以读取哪些字段,其他的调用为默认值,但是实际作用应该不大
// 假设 isAdmin 的默认值为 false
console.log(user.username); // 'alice123'
console.log(user.isAdmin); // false
2
3
4
5
6
7
# SELECT
- 使用
findAll()
查询一个表中的所有字段,等价于SELECT * FROM TABLE_NAME
- 加入
attributes
属性,可以查询表中的对应字段,attributes
一定要为数组,等价于SELECT attribute1, attribute2, ... FROM TABLE_NAME
- 在数组中嵌套数组表示重命名
Model.findAll({
attributes: ['foo', ['bar', 'baz'], 'qux']
});
2
3
SELECT foo, bar AS baz, qux FROM ...
attributes
中可以加入聚合属性,聚合属性必须要有对应的别名
Model.findAll({
attributes: [
'foo',
[sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats'],
'bar'
]
});
2
3
4
5
6
7
SELECT foo, COUNT(hats) AS n_hats, bar FROM ...
只想添加聚合时,可以使用
include
属性想要排除某些字段时,可以使用
exclude
属性where
可以传递查询条件,默认为相等,为 [Op.eq]除此之外,sequelize的Op属性中还有非常多的操作符,具体使用时可以关注模型查询(基础) (opens new window)
# UPDATE
第一个参数传递要修改的值,第二个参数可以传递一些选项,比如 where
和 transaction
// 将所有没有姓氏的人更改为 "Doe"
await User.update({ lastName: "Doe" }, {
where: {
lastName: null
}
});
2
3
4
5
6
# DELETE
- 根据条件删除可以传入
where
- 删除所有条目可以使用
truncate
// 截断表格
await User.destroy({
truncate: true
});
2
3
4
# 批量创建
Model.bulkCreate
允许在一次查询中创建多个记录,可以在 bulkCreate
方法中添加参数 validate
来为每个对象进行检验,但是只有所有都通过之后才会创建成功
const Foo = sequelize.define('foo', {
name: {
type: DataTypes.TEXT,
validate: {
len: [4, 6]
}
}
});
// 这不会引发错误,两个实例都将被创建
await Foo.bulkCreate([
{ name: 'abc123' },
{ name: 'name too long' }
]);
// 这将引发错误,不会创建任何内容
await Foo.bulkCreate([
{ name: 'abc123' },
{ name: 'name too long' }
], { validate: true });
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 同时
bulkCreate
也可以加入fields
参数
# 排序
向查询中添加 order 可以选择对应的排序方式
Subtask.findAll({
order: [
// 将转义 title 并针对有效方向列表进行降序排列
['title', 'DESC'],
// 将按最大年龄进行升序排序
sequelize.fn('max', sequelize.col('age')),
// 将按最大年龄进行降序排序
[sequelize.fn('max', sequelize.col('age')), 'DESC'],
// 将按 otherfunction(`col1`, 12, 'lalala') 进行降序排序
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
// 将使用模型名称作为关联名称按关联模型的 createdAt 排序.
[Task, 'createdAt', 'DESC'],
// 将使用模型名称作为关联名称通过关联模型的 createdAt 排序.
[Task, Project, 'createdAt', 'DESC'],
// 将使用关联名称按关联模型的 createdAt 排序.
['Task', 'createdAt', 'DESC'],
// 将使用关联的名称按嵌套的关联模型的 createdAt 排序.
['Task', 'Project', 'createdAt', 'DESC'],
// 将使用关联对象按关联模型的 createdAt 排序. (首选方法)
[Subtask.associations.Task, 'createdAt', 'DESC'],
// 将使用关联对象按嵌套关联模型的 createdAt 排序. (首选方法)
[Subtask.associations.Task, Task.associations.Project, 'createdAt', 'DESC'],
// 将使用简单的关联对象按关联模型的 createdAt 排序.
[{model: Task, as: 'Task'}, 'createdAt', 'DESC'],
// 将由嵌套关联模型的 createdAt 简单关联对象排序.
[{model: Task, as: 'Task'}, {model: Project, as: 'Project'}, 'createdAt', 'DESC']
],
// 将按最大年龄降序排列
order: sequelize.literal('max(age) DESC'),
// 如果忽略方向,则默认升序,将按最大年龄升序排序
order: sequelize.fn('max', sequelize.col('age')),
// 如果省略方向,则默认升序, 将按年龄升序排列
order: sequelize.col('age'),
// 将根据方言随机排序(但不是 fn('RAND') 或 fn('RANDOM'))
order: sequelize.random()
});
Foo.findOne({
order: [
// 将返回 `name`
['name'],
// 将返回 `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'],
// 将返回 otherfunction(awesomefunction(`col`)) DESC, 这种嵌套可能是无限的!
[sequelize.fn('otherfunction', sequelize.fn('awesomefunction', sequelize.col('col'))), 'DESC']
]
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
回顾一下,order 数组的元素可以如下: 一个字符串 (它将被自动引用), 一个数组, 其第一个元素将被引用,第二个将被逐字追加, 一个具有 raw 字段的对象:raw 内容将不加引用地逐字添加,其他所有内容都将被忽略,如果未设置 raw,查询将失败 调用 Sequelize.fn (这将在 SQL 中生成一个函数调用), 调用 Sequelize.col (这将引用列名),
# 分组
分组和排序的语法几乎一样,但是没有升序和降序之别
注意
你还可以将字符串直接传递给 group,该字符串将直接(普通)包含在生成的 SQL 中. 请谨慎使用,请勿与用户生成的内容一起使用.
# 限制与分页
在查询中,加入 limit
可以限制查询的最多数目,加入 offset
可以声明开始查询的条目
# 实用方法
- count 计算数据库中元素出现的数目
console.log(`这有 ${await Project.count()} 个项目`);
const amount = await Project.count({
where: {
id: {
[Op.gt]: 25
}
}
});
console.log(`这有 ${amount} 个项目 id 大于 25`);
2
3
4
5
6
7
8
9
10
- max min sum
await User.max('age'); // 40
await User.max('age', { where: { age: { [Op.lt]: 20 } } }); // 10
await User.min('age'); // 5
await User.min('age', { where: { age: { [Op.gt]: 5 } } }); // 10
await User.sum('age'); // 55
await User.sum('age', { where: { age: { [Op.gt]: 5 } } }); // 50
2
3
4
5
6
- increment,decrement (前面已有)
# 模型查找器
findAll
查询所有条目findByPk
根据主键进行查找findOne
查找满足条件的第一个条目findOrCreate
除非找到一个满足查询参数的结果,否则方法 findOrCreate 将在表中创建一个条目. 在这两种情况下,它将返回一个实例(找到的实例或创建的实例)和一个布尔值,指示该实例是已创建还是已经存在.
提示
使用 where 参数来查找条目,而使用 defaults 参数来定义必须创建的内容. 如果 defaults 不包含每一列的值,则 Sequelize 将采用 where 的值(如果存在). 假设我们有一个空的数据库,该数据库具有一个 User 模型,该模型具有一个 username 和一个 job.
const [user, created] = await User.findOrCreate({
where: { username: 'sdepold' },
defaults: {
job: 'Technical Lead JavaScript'
}
});
console.log(user.username); // 'sdepold'
console.log(user.job); // 这可能是也可能不是 'Technical Lead JavaScript'
console.log(created); // 指示此实例是否刚刚创建的布尔值
if (created) {
console.log(user.job); // 这里肯定是 'Technical Lead JavaScript'
}
2
3
4
5
6
7
8
9
10
11
12
findAndCountAll
结合了 findAll 和 count 的便捷方法. 在处理与分页有关的查询时非常有用,在分页中,你想检索带有 limit 和 offset 的数据,但又需要知道与查询匹配的记录总数.
提示
当提供了 group 时, findAndCountAll 方法返回一个具有两个属性的对象:
count - 一个数组对象 - 包含每组中的合计和预设属性 rows - 一个数组对象 - 获得的记录
提示
至此,已经基本学习了 sequelize
的基本语法,剩下的内容更适合阅读学习,只有遇到场景时才需要查阅。