Sequelize belongsToMany关系的使用及 is not associated with 问题的处理

 2016年09月15日    428     声明


belongsToMany用于表示模型间N:M(多对多)的关系,N:M关系通过一个“关系表”建立两个模型间的关联关系。用于建立关联关系的“关系表”可以手工创建,也可以由Sequelize自动创建。

  1. 模型的定义
  2. 模型间的关联关系
  3. N:M关联模型的查询

在个项目中,有用户、角色、用户角色关系三张表,三张表对应的模型及模型间的关系如下:

  • 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);
})