乐趣区

Sequelizejs-关联

One-To-One

看似一对一, 其实一对多. 这里的 One-To-One 指的应该是查询数据 (主表) 结果中, 关联信息是以 单个 形式作为一个属性挂在主表 每一个 对象中

实际上是, 主表与关联表的多对一关系.

belongsTo

SourceModel.belongsTo(TargetModel, { as, foreignKey, targetKey})

拿 SourceModel 中的 foreignKey 和 TargetModel 中的 targetKey 进行关联.
as 配置 TargetModel 的别名
foreignKey 配置 SourceModel 中的外键字段名称,默认为 ${as || TargetModel.name}+${TargetModel.primaryKey}
targetKey 配置 TargetModel 中的目标键字段名称,默认为 TargetModel 主键

查询出来结果结构如:

const sourceObj = {
  <sourceObjAttr>: <value>,
  <sourceObjAttr>: <value>,
  ...
  <as || TargetModel.name>: targetObj
}

SourceModel 中存在 foreignKey 关联 TargetModel, 比如:

Player.belongsTo(Team)

foreignKey 用来自定义 SourceModel 中的外键名称. 比如:

User.belongsTo(Company, { foreignKey: 'cid'}) // User.cid 即外键

默认情况下,foreignKey 值为${Team.name}+${Team.primaryKey}. 注意这里的连接是根据 source model 的配置而决定是 camelCase 还是 underscored. 例如:

const User = this.sequelize.define('user', {}, {underscored: true})
const Company = this.sequelize.define('company', {uuid: { type: Sequelize.UUID, primaryKey: true}
})

User.belongsTo(Company) // User 的 foreignKey 为 company_uuid

targetKey 用来指定 targetModel 中用来和 sourceModel 中外键进行匹配的字段名称, 默认为 targetModel 中的 primaryKey. 比如:

User.belongsTo(Company, { foreignKey: 'cid', targetKey: 'uuid'}) // 即将 User.cid 与 Company.uuid 进行关联

as 用来转换 targetModel 的 name. 这里有两个方面, 一个是在生成 foreignKey 时替换 targetModel name, 一个是在拼装查询结果时, 改变 targetModel 作为 sourceModel 中的属性的名称. 比如:

User.belongsTo(Company, { as: 'com'})

// foreign key would be: com_uuid
// company property of user object would be 'com'

hasOne

SourceModel.hasOne(TargetModel, { as, foreignKey})

拿 SourceModel 中的主键和 TargetModel 中的外键进行关联
as 配置 TargetModel 的别名
foreignKey 配置 TargetModel 中的外键字段名称,默认为 ${as || SourceModel.name}+${SourceModel.primaryKey}

查询结构为:

const sourceObj = {
  <sourceObjAttr>: <value>,
  <sourceObjAttr>: <value>,
  ...
  <as || TargetModel.name>: targetObj
}

TargetModel 中存在 foreignKey 关联 SourceModel, 比如:

Company.hasOne(User)

foreignKey 默认为 ${Company.name}+${Company.primaryKey}. 也可以自定义:

Company.hasOne(User, { foreignKey: 'company_id'}) // User.company_id 为外键

targetKey hasOne 中没有 targetKey 属性

belongsTo V.S hasOne

如果关联信息 (比如: 外键) 保存在 source 中, 就是用belongsTo; 如果关联信息保存在 target 中, 则是用hasOne

比如:

  1. Player 表,teamId 关联 Team 表
  2. Coach 表
  3. Team 表,coachId 关联 Coach 表
Player.belongsTo(Team) // Player.teamId -- Team.id
Coach.hasOne(Team) // Team.coachId -- Coach.id

为什么不能反过来, 比如 Player.belongsTo(Team) 中将 source 和 target 交换下, 不就可以应用 hasOne 了吗? 问题就在于 source 是根据查询要求而定 的, 如果是要查询 Player, 并把关联的 Team 信息带出来, 那么 source 就只能是 Player.

小结: 根据查询主表, 即 source, 找到需要查询到的关联表, 如果关联信息 (外键) 在 source 中, 则 belongsTo, 否则 hasOne.

One-To-Many

hasMany

SourceModel.hasMany(TargetModel, { as, foreignKey, sourceKey})

拿 TargetModel 中的外键与 SourceModal 中的主键关联
as 配置 TargetModel 别名
foreignKey 配置 TargetModel 中外键名称,默认为 ${SourceModel.name}+${SourceMode.primaryKey}
soruceKey 配置 SourceModel 中关联键名称,默认为 SourceModel.primaryKey

这里 as 的值并不影响 key 的默认值

Many-To-Many

belongsToMany

SourceModel.belongsToMany(TargetModel, { through: AssociationModel, as, foreignKey, otherKey})

通过中间表进行关联
as 中间表 AssociationModel 的别名
foreignKey 配置 AssociationModel 中与 SourceModel 主键关联的外键名称,默认为 SourceModel.name + SourceModel.primaryKey
otherKey 配置 AssociationModel 中与 TargetModel 主键关联的外键名称,默认为 TargetModel.name + TargetModel.primaryKey

这里 as 的值并不影响 key 的默认值

User.belongsToMany(Role, { through: RoleAssignment, foreignKey: 'userId', otherKey: 'roleId'})

user.findAll({
  include: [{
    model: Role,
    through: {// attributes: [], // uncomment this line to hide RoleAssignment as attribute in result in target model
      model: RoleAssignment,
      where
    }
  }]
})

返回结果

const result = [{
  id: '',
  userName: '',
  gender: '',
  roles: [{
    id: '',
    roleName: '',
    ...
    RoleAssignment: {
      id: '',
      roleId: '',
      userId: '',
      ...
    }
  }]
}]
退出移动版