预加载
当你从数据库检索数据时,也想同时获得与之相关联的查询,这被称为预加载.这个基本思路就是当你调用 find
或 findAll
时使用 include
属性.让我们假设以下设置:
class User extends Model {}
User.init({ name: Sequelize.STRING }, { sequelize, modelName: 'user' })
class Task extends Model {}
Task.init({ name: Sequelize.STRING }, { sequelize, modelName: 'task' })
class Tool extends Model {}
Tool.init({ name: Sequelize.STRING }, { sequelize, modelName: 'tool' })
Task.belongsTo(User)
User.hasMany(Task)
User.hasMany(Tool, { as: 'Instruments' })
sequelize.sync().then(() => {
// 这是我们继续的地方 ...
})
首先,让我们用它们的关联 user 加载所有的 task.
Task.findAll({ include: [ User ] }).then(tasks => {
console.log(JSON.stringify(tasks))
/*
[{
"name": "A Task",
"id": 1,
"createdAt": "2013-03-20T20:31:40.000Z",
"updatedAt": "2013-03-20T20:31:40.000Z",
"userId": 1,
"user": {
"name": "John Doe",
"id": 1,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z"
}
}]
*/
})
请注意,访问者(结果实例中的 User
属性)是单数形式,因为关联是一对一的.
接下来的事情:用多对一的关联加载数据!
User.findAll({ include: [ Task ] }).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",
"tasks": [{
"name": "A Task",
"id": 1,
"createdAt": "2013-03-20T20:31:40.000Z",
"updatedAt": "2013-03-20T20:31:40.000Z",
"userId": 1
}]
}]
*/
})
请注意,访问者(结果实例中的 Tasks
属性)是复数形式,因为关联是多对一的.
如果关联是别名的(使用 as
参数),则在包含模型时必须指定此别名. 注意用户的 Tool
如何被别名为 Instruments
. 为了获得正确的权限,你必须指定要加载的模型以及别名:
User.findAll({ include: [{ model: Tool, as: 'Instruments' }] }).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": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}]
*/
})
你还可以通过指定与关联别名匹配的字符串来包含别名:
User.findAll({ include: ['Instruments'] }).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": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}]
*/
})
User.findAll({ include: [{ association: 'Instruments' }] }).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": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}]
*/
})
当预加载时,我们也可以使用 where
过滤关联的模型. 这将返回 Tool
模型中所有与 where
语句匹配的行的User
.
User.findAll({
include: [{
model: Tool,
as: 'Instruments',
where: { name: { [Op.like]: '%ooth%' } }
}]
}).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": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}],
[{
"name": "John Smith",
"id": 2,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}],
*/
})
当使用 include.where
过滤一个预加载的模型时,include.required
被隐式设置为 true
. 这意味着内部联接完成返回具有任何匹配子项的父模型.
使用预加载模型的顶层 WHERE
将模型的 WHERE
条件从 ON
条件的 include 模式移动到顶层,你可以使用 '$nested.column$'
语法:
User.findAll({
where: {
'$Instruments.name$': { [Op.iLike]: '%ooth%' }
},
include: [{
model: Tool,
as: 'Instruments'
}]
}).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": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}],
[{
"name": "John Smith",
"id": 2,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}],
*/
包括所有
要包含所有属性,你可以使用 all:true
传递单个对象:
User.findAll({ include: [{ all: true }]});
包括软删除的记录
如果想要加载软删除的记录,可以通过将 include.paranoid
设置为 false
来实现
User.findAll({
include: [{
model: Tool,
where: { name: { [Op.like]: '%ooth%' } },
paranoid: false // query and loads the soft deleted records
}]
});
排序预加载关联
在一对多关系的情况下.
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 }]});
使用 right join 进行关联
默认情况下, 关联是使用 left join 加载的, 也就是说, 它仅包括来自父表的记录. 如果您使用的方言支持,可以通过传递 right
属性来将这种行为更改为右连接. 目前, sqlite
不支持right joins.
注意: 仅当 required
是 false 时才遵循 right
.
User.findAll({
include: [{
model: Tool // 将创建 left join
}]
});
User.findAll({
include: [{
model: Tool,
right: true // 将创建 right join
}]
});
User.findAll({
include: [{
model: Tool,
required: true,
right: true // 没有作用, 将创建一个 inner join
}]
});
User.findAll({
include: [{
model: Tool,
where: { name: { [Op.like]: '%ooth%' } },
right: true // 没有作用, 将创建一个 inner join
}]
});
User.findAll({
include: [{
model: Tool,
where: { name: { [Op.like]: '%ooth%' } },
required: false
right: true // 因为我们设置 `required` 为 false, 这将创建一个 right join
}]
});