belongsToMany
用于表示模型间N:M
(多对多)的关系,N:M
关系通过一个“关系表”建立两个模型间的关联关系。用于建立关联关系的“关系表”可以手工创建,也可以由Sequelize自动创建。
在个项目中,有用户、角色、用户角色关系三张表,三张表对应的模型及模型间的关系如下:
Users
-用户模型,一个用户可以拥有多种角色Roles
-角色模型,每个角色都可以属性多个用户UserRoleMapping
-用户角色关系模型,用于表示用户/角色之间的关联关系
1. 模型的定义
三个模型定义如下:
Users
模型:
module.exports = function (sequelize, DataTypes) { return sequelize.define('Users', { id: { type:DataTypes.BIGINT(11), autoIncrement:true, primaryKey : true, unique : true}, username: { type: DataTypes.STRING, allowNull: false, comment:'用户名' }, password: { type: DataTypes.STRING, allowNull: false, comment:'用户密码' }, name: { type: DataTypes.STRING, comment:'姓名' }, nickname: { type: DataTypes.STRING, comment:'昵称' }, createdAt: { type: DataTypes.DATE, field: 'created_at', allowNull: false, defaultValue: DataTypes.NOW }, updatedAt: { type: DataTypes.DATE, field: 'updated_at', allowNull: false, defaultValue: DataTypes.NOW } }, { timestamps: false, underscore: false, freezeTableName: true, tableName: 'users', charset: 'utf8', collate: 'utf8_general_ci' }); }
Roles
模型:
module.exports = function (sequelize, DataTypes) { return sequelize.define('Roles', { id: { type: DataTypes.BIGINT(11), autoIncrement: true, primaryKey: true, unique: true, comment:'角色Id' }, roleName: { type: DataTypes.STRING, field: 'role_name', comment:'角色名' } }, { underscore: false, timestamps: false, freezeTableName: true, tableName: 'roles', charset: 'utf8', collate: 'utf8_general_ci' }); }
UserRoleMapping
模型:
module.exports = function (sequelize, DataTypes) { return sequelize.define('UserRoleMapping', { userId: {type: DataTypes.BIGINT(11), field: 'user_id', primaryKey: true, allowNull: false, comment:'用户Id' }, roleId: {type: DataTypes.BIGINT(11), field: 'role_id', primaryKey: true, allowNull: false, comment:'角色Id' } }, { underscore: false, timestamps: false, freezeTableName: true, tableName: 'userRoleMapping', charset: 'utf8', collate: 'utf8_general_ci' }); }
2. 模型间的关联关系
定义模型后,引入定义好的模型,并指定模型间的关联关系:
var sequelize=require('./db').sequelize(); var User = sequelize.import('./user/users'); var Role = sequelize.import('./user/roles'); var UserRoleMapping = sequelize.import('./user/userRoleMapping'); // 定义 N:M 关系 User.belongsToMany(Role, {through:UserRoleMapping, as:'UserRoles', foreignKey:'user_id', otherKey:'role_id'}); Role.belongsToMany(User, {through:UserRoleMapping, as:'UserRoles', foreignKey:'role_id', otherKey:'user_id'});
在N:M
关联关系模型中,关系表并不需要显式的创建,通过belongsToMany
指定关联关系后Sequelize会自动创建关系,但所创建的关系表名称并不一定符合逻辑,所以一般会通过through
显式指定关系表(或关系模型)。除指定了关联模型外,在上例中还通过as
参数为关系表指定了别名。
在定义关系模型时,我们并没有指定外键关系,所以指定模型间的关联关系时,要通过foreignKey
指定模型在关系表中的外键,并通过otherKey
指定关联表在关系表中的外键。
3. N:M
关联模型的查询
在N:M
关联关系的模型中,要同时查询关系模型中的数据。只需要在查询参数中指定include
查询参数即可,指定后Sequelize会自动生成连接查询SQL语句。
如,查询所有用户及用户角色:
User.findAll({include:[{model:Role}]}).then(function (results) { console.log(results) }).catch(function(err){ console.log(err); })
在前面定义模型belongsToMany
关联关系时,通过as
参数指定UserRoleMapping
的别名为UserRoles
,但在查询时并没有使用别名。以上查询会产生以下错误:
Error: Roles is not associated to Users!]
解决这个问题有以下两种处理方式:
定义模型关系及查询时全部使用as
别名:
User.belongsToMany(Role, {through:UserRoleMapping, as:'UserRoles', foreignKey:'user_id', otherKey:'role_id'}); User.findAll({include:[{model:Role, as:'UserRoles'}]}).then(function (results) { console.log(results) }).catch(function(err){ console.log(err); })
或者,定义模型关系及查询时全部不使用as
别名:
User.belongsToMany(Role, {through:UserRoleMapping, foreignKey:'user_id', otherKey:'role_id'}); User.findAll({include:[{model:Role}]}).then(function (results) { console.log(results) }).catch(function(err){ console.log(err); })