如何用sequelize提供的方法,实现多对多联表查询?
我们以 RBAC(Role-Based Access Control,基于角色的访问控制)来举例说明。
简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。(如下图)
这里我们先以用户表和角色表的关联查询为例子,我们引入了一个用户角色关联表,来绑定用户id和角色id之间的对应关系。
模型的定义
数据库中的表之间存在一定的关联关系,表之间的关系基于主/外键进行关联、创建约束等。关系表中的数据分为1对1(1:1)、1对多(1:M)、多对多(N:M)三种关联关系。
在Sequelize中建立关联关系,通过调用模型(源模型)的belongsTo、hasOne、hasMany、belongsToMany方法,再将要建立关系的模型(目标模型)做为参数传入即可。
model建好后,需要在model导出前,通过model.associate方法来指定表和表之间的关联关系。而在有中间表的情况下,可以直接在中间表里指定了关系,另外两张表就不需要去重复指定了。具体代码如下:
用户表模型:app.model.Users
'use strict';
const moment = require('moment');
module.exports = app => {
const { STRING, INTEGER, DATE} = app.Sequelize;
const User = app.model.define('users', {
id: { type: INTEGER, primaryKey: true, autoIncrement: true }, // 主键,自增
username: { type: STRING(30), allowNull: false, unique: true }, // 不允许为空,必须唯一
mobile: { type: STRING}, // 手机号
created_at: {
type: DATE,
get() {
//获取时间时格式化
return moment(this.getDataValue('created_at')).format('YYYY-MM-DD HH:mm:ss');
},
},
updated_at: {
type: DATE,
get() {
return moment(this.getDataValue('created_at')).format('YYYY-MM-DD HH:mm:ss');
},
},
}, {
freezeTableName: true, // 防止修改表名为复数
});
return User;
};
角色表模型:app.model.Role
'use strict';
module.exports = app => {
const { STRING, INTEGER } = app.Sequelize;
const Role = app.model.define('role', {
id: { type: INTEGER, primaryKey: true, autoIncrement: true }, // 主键,自增
name: { type: STRING(30), unique: true },
describe: STRING(60),
}, {
freezeTableName: true,
});
return Role;
};
用户角色关联表模型:app.model.UsersRole
'use strict';
module.exports = app => {
const { INTEGER, DATE } = app.Sequelize;
const UsersRole = app.model.define('users_role', {
id: { type: INTEGER, primaryKey: true, autoIncrement: true }, // 主键,自增
users_id: { type: INTEGER },
role_id: { type: INTEGER },
created_at: DATE,
updated_at: DATE,
}, {
freezeTableName: true,
});
UsersRole.associate = function() {
// 用belongsToMany指定多对多关联关系,并指定外键
app.model.User.belongsToMany(app.model.Role, {
through: UsersRole,
foreignKey: 'users_id',
otherKey: 'role_id',
});
app.model.Role.belongsToMany(app.model.User, {
through: UsersRole,
foreignKey: 'role_id',
otherKey: 'users_id',
});
};
return UsersRole;
};
在controller.Users里实现联表查询
在findAll方法里,通过include参数引入需要关联的表,这时候这两个表的外键早已在创建用户角色模型时指定好了,直接引入角色模型就可以实现联表查询。
async getUserList() {
const { ctx } = this;
const id = toInt(ctx.request.body.userId);
const users = await ctx.model.User.findAll({
include: [
{ model: ctx.model.Role },
],
where: {
id,
},
});
if (!user) {
ctx.status = 404;
return;
}
ctx.body = {result:users}
}
查询出来的结果,会在users列表里的每个user里,会多出一个roles的角色列表
"result": [
{
"created_at": "2022-02-23 13:27:50",
"updated_at": "2022-02-23 13:27:50",
"id": 999,
"username": "Tony",
"roles": [
{
"id": 1,
"name": "超级管理",
"describe": "eee23456",
"createdAt": "2022-02-23T02:59:21.000Z",
"updatedAt": "2022-02-24T06:41:33.000Z",
"users_role": {
"id": 1,
"users_id": 999,
"role_id": 1,
"createdAt": "2022-02-24T08:13:38.000Z",
"updatedAt": "2022-02-24T08:13:42.000Z"
}
}
]
}
]
这里我们只讨论了复杂点的多对多情况,其他联表方法可以参考官网文档:【点击跳转到sequlize文档】