Sequelize V5.9.4 MVC模式(七 | 细节补充)
大家可以前往 Sequelize中文文档,查看 Sequelize不同版本【5.x、4.x】的文档
本文档分多个篇章,难易程度从低到高,学习此篇章之前,务必确保自己已经掌握 node.js、express、es6语法、mysql等关系型数据库的sql语法等
细节补充
数据检索/查找器
findByPk
// 搜索已知的ids
Project.findByPk(123).then(project => {
// project 将是 Project的一个实例,并具有在表中存为 id 123 条目的内容.
// 如果没有定义这样的条目,你将获得null
})
findOrCreate
:搜索特定元素或创建它(如果不可用)
User
.findOrCreate({where: {username: 'Mjhu'}, defaults: {job: 'Technical Lead JavaScript'}})
. then(([user, created]) => {
console.log(user.get({
plain: true
}))
console.log(created)
/*
findOrCreate 返回一个包含已找到或创建的对象的数组,找到或创建的对象和一个布尔值,如果创建一个新对象将为true,否则为false,像这样:
[ {
username: 'Mjhu',
job: 'Technical Lead JavaScript',
id: 1,
createdAt: '2019-07-12T10:45:46.000Z'
updatedAt: '2019-07-12T10:45:46.000Z'
},
true ]
在上面的例子中,第三行的数组将分成2部分,并将它们作为参数传递给回调函数,在这种情况下将它们视为 "user" 和 "created" .(所以“user”将是返回数组的索引0的对象,并且 "created" 将等于 "true".)
*/
})
排除指定的字段
Model.findAll({
attributes: { exclude: ['createdAt','updatedAt'] } //除了排除的这两个字段,其他字段都返回
});
运算符安全性
默认情况下,Sequelize 将使用 Symbol 运算符. 使用没有任何别名的 Sequelize 可以提高安全性.没有任何字符串别名将使得运算符可能被注入的可能性降到极低,但你应该始终正确验证和清理用户输入.
一些框架会自动将用户输入解析为js对象,如果你无法清理输入,则可能会将带有字符串运算符的 Object
注入Sequelize
.
为了更好的安全性,强烈建议在代码中使用 Sequelize.Op
中的符号运算符,如Op.and
/ Op.or
,而不依赖于任何基于字符串的运算符,如 $and
/ $or
. 你可以通过设置 operatorsAliases
参数来限制应用程序所需的别名,记住清理用户输入,特别是当你直接将它们传递给 Sequelize
方法时.
const Op = Sequelize.Op;
// 不用任何操作符别名使用 sequelize
const connection = new Sequelize(db, user, pass, { operatorsAliases: false });
// 只用 $and => Op.and 操作符别名使用 sequelize
const connection2 = new Sequelize(db, user, pass, { operatorsAliases: { $and: Op.and } });
排序
Subtask.findAll({
order: [
// 将转义标题,并根据有效的方向参数列表验证DESC
['title', 'DESC'],
// 将按最大值排序(age)
sequelize.fn('max', sequelize.col('age')),
// 将按最大顺序(age) DESC
[sequelize.fn('max', sequelize.col('age')), 'DESC'],
// 将按 otherfunction 排序(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
// 将使用模型名称作为关联的名称排序关联模型的 created_at.
[Task, 'createdAt', 'DESC'],
// Will order through an associated model's created_at using the model names as the associations' names.
[Task, Project, 'createdAt', 'DESC'],
// 将使用关联的名称由关联模型的created_at排序.
['Task', 'createdAt', 'DESC'],
// Will order by a nested associated model's created_at using the names of the associations.
['Task', 'Project', 'createdAt', 'DESC'],
// Will order by an associated model's created_at using an association object. (优选方法)
[Subtask.associations.Task, 'createdAt', 'DESC'],
// Will order by a nested associated model's created_at using association objects. (优选方法)
[Subtask.associations.Task, Task.associations.Project, 'createdAt', 'DESC'],
// Will order by an associated model's created_at using a simple association object.
[{model: Task, as: 'Task'}, 'createdAt', 'DESC'],
// 嵌套关联模型的 created_at 简单关联对象排序
[{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()
})
原始查询
有时候,你可能会期待一个你想要显示的大量数据集,而无需操作. 对于你选择的每一行,Sequelize 创建一个具有更新,删除和获取关联等功能的实例.如果你有数千行,则可能需要一些时间. 如果你只需要原始数据,并且不想更新任何内容,你可以这样做来获取原始数据.
// 你期望从数据库的一个巨大的数据集,
// 并且不想花时间为每个条目构建DAO?
// 你可以传递一个额外的查询参数来取代原始数据:
Project.findAll({ where: { ... }, raw: true })
count
- 计算数据库中元素的出现次数
//这里是获取属性的最大值的方法:
/*
我们假设3个具有属性年龄的对象.
第一个是10岁,
第二个是5岁,
第三个是40岁.
*/
Project.max('age').then(max => {
// 将返回 40
})
Project.max('age', { where: { age: { [Op.lt]: 20 } } }).then(max => {
// 将会是 10
})
min
- 获取特定表中特定属性的最小值
这里是获取属性的最小值的方法:
/*
我们假设3个具有属性年龄的对象.
第一个是10岁,
第二个是5岁,
第三个是40岁.
*/
Project.min('age').then(min => {
// 将返回 5
})
Project.min('age', { where: { age: { [Op.gt]: 5 } } }).then(min => {
// 将会是 10
})
sum
- 特定属性的值求和
为了计算表的特定列的总和,可以使用sum
方法.
/*
我们假设3个具有属性年龄的对象.
第一个是10岁,
第二个是5岁,
第三个是40岁.
*/
Project.sum('age').then(sum => {
// 将返回 55
})
Project.sum('age', { where: { age: { [Op.gt]: 5 } } }).then(sum => {
// 将会是 50
})
排序预加载关联
在一对多关系的情况下.
Company.findAll({ include: [ Division ], order: [ [ Division, 'name' ] ] });
Company.findAll({ include: [ Division ], order: [ [ Division, 'name', 'DESC' ] ] });
Company.findAll({
include: [ { model: Division, as: 'Div' } ],
order: [ [ { model: Division, as: 'Div' }, 'name' ] ]
});
Company.findAll({
include: [ { model: Division, as: 'Div' } ],
order: [ [ { model: Division, as: 'Div' }, 'name', 'DESC' ] ]
});
Company.findAll({
include: [ { model: Division, include: [ Department ] } ],
order: [ [ Division, Department, 'name' ] ]
});
在多对多关系的情况下,你还可以通过表中的属性进行排序.
Company.findAll({
include: [ { model: Division, include: [ Department ] } ],
order: [ [ Division, DepartmentDivision, 'name' ] ]
});
嵌套预加载
你可以使用嵌套的预加载来加载相关模型的所有相关模型:
User.findAll({
include: [
{model: Tool, as: 'Instruments', include: [
{model: Teacher, include: [ /* etc */]}
]}
]
}).then(users => {
console.log(JSON.stringify(users))
/*
[{
"name": "John Doe",
"id": 1,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{ // 1:M and N:M association
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1,
"Teacher": { // 1:1 association
"name": "Jimi Hendrix"
}
}]
}]
*/
})
这将产生一个外连接. 但是,相关模型上的 where
语句将创建一个内部连接,并仅返回具有匹配子模型的实例. 要返回所有父实例,你应该添加 required: false
.
User.findAll({
include: [{
model: Tool,
as: 'Instruments',
include: [{
model: Teacher,
where: {
school: "Woodstock Music School"
},
required: false
}]
}]
}).then(users => {
/* ... */
})
以上查询将返回所有用户及其所有乐器,但只会返回与 Woodstock Music School 相关的老师.
包括所有也支持嵌套加载:
User.findAll({ include: [{ all: true, nested: true }]});
到这里,
Sequelize V5.9.4
的大部分常用的知识就介绍完毕了。更多细节请前往 Sequelize 中文文档