一对一关联

一对一关联是通过单个外键连接的两个模型之间的关联.

BelongsTo

BelongsTo 关联是在 source model 上存在一对一关系的外键的关联.

一个简单的例子是 Player 通过 player 的外键作为 Team 的一部分.

  1. class Player extends Model {}
  2. Player.init({/* attributes */}, { sequelize, modelName: 'player' });
  3. class Team extends Model {}
  4. Team.init({/* attributes */}, { sequelize, modelName: 'team' });
  5. Player.belongsTo(Team); // 将向 Player 添加一个 teamId 属性以保存 Team 的主键值

外键

默认情况下,将从目标模型名称和目标主键名称生成 belongsTo 关系的外键.

默认的样式是 camelCase,但是如果源模型配置为 underscored: true ,那么将使用字段 snake_case 创建 foreignKey.

  1. class User extends Model {}
  2. User.init({/* attributes */}, { sequelize, modelName: 'user' })
  3. class Company extends Model {}
  4. Company.init({/* attributes */}, { sequelize, modelName: 'company' });
  5. // 将 companyId 添加到 user
  6. User.belongsTo(Company);
  7. class User extends Model {}
  8. User.init({/* attributes */}, { underscored: true, sequelize, modelName: 'user' })
  9. class Company extends Model {}
  10. Company.init({
  11. uuid: {
  12. type: Sequelize.UUID,
  13. primaryKey: true
  14. }
  15. }, { sequelize, modelName: 'company' });
  16. // 将 companyUuid 以 company_uuid 名称添加到 user
  17. User.belongsTo(Company);

在已定义 as 的情况下,将使用它代替目标模型名称.

  1. class User extends Model {}
  2. User.init({/* attributes */}, { sequelize, modelName: 'user' })
  3. class UserRole extends Model {}
  4. UserRole.init({/* attributes */}, { sequelize, modelName: 'userRole' });
  5. User.belongsTo(UserRole, {as: 'role'}); // 将 role 添加到 user 而不是 userRole

在所有情况下,默认外键可以用 foreignKey 选项覆盖. 当使用外键选项时,Sequelize 将按原样使用:

  1. class User extends Model {}
  2. User.init({/* attributes */}, { sequelize, modelName: 'user' })
  3. class Company extends Model {}
  4. Company.init({/* attributes */}, { sequelize, modelName: 'company' });
  5. User.belongsTo(Company, {foreignKey: 'fk_company'}); // 将 fk_company 添加到 User

目标键

目标键是源模型上的外键列指向的目标模型上的列. 默认情况下,belongsTo 关系的目标键将是目标模型的主键. 要定义自定义列,请使用 targetKey 选项.

  1. class User extends Model {}
  2. User.init({/* attributes */}, { sequelize, modelName: 'user' })
  3. class Company extends Model {}
  4. Company.init({/* attributes */}, { sequelize, modelName: 'company' });
  5. User.belongsTo(Company, {foreignKey: 'fk_companyname', targetKey: 'name'}); // 添加 fk_companyname 到 User

HasOne

HasOne 关联是在 target model 上存在一对一关系的外键的关联.

  1. class User extends Model {}
  2. User.init({/* ... */}, { sequelize, modelName: 'user' })
  3. class Project extends Model {}
  4. Project.init({/* ... */}, { sequelize, modelName: 'project' })
  5. // 单向关联
  6. Project.hasOne(User)
  7. /*
  8. 在此示例中,hasOne 将向 User 模型添加一个 projectId 属性 !
  9. 此外,Project.prototype 将根据传递给定义的第一个参数获取 getUser 和 setUser 的方法.
  10. 如果启用了 underscore 样式,则添加的属性将是 project_id 而不是 projectId.
  11. 外键将放在 users 表上.
  12. 你也可以定义外键,例如 如果你已经有一个现有的数据库并且想要处理它:
  13. */
  14. Project.hasOne(User, { foreignKey: 'initiator_id' })
  15. /*
  16. 因为Sequelize将使用模型的名称(define的第一个参数)作为访问器方法,
  17. 还可以将特殊选项传递给hasOne:
  18. */
  19. Project.hasOne(User, { as: 'Initiator' })
  20. // 现在你可以获得 Project.getInitiator 和 Project.setInitiator
  21. // 或者让我们来定义一些自己的参考
  22. class Person extends Model {}
  23. Person.init({ /* ... */}, { sequelize, modelName: 'person' })
  24. Person.hasOne(Person, {as: 'Father'})
  25. // 这会将属性 FatherId 添加到 Person
  26. // also possible:
  27. Person.hasOne(Person, {as: 'Father', foreignKey: 'DadId'})
  28. // 这将把属性 DadId 添加到 Person
  29. // 在这两种情况下,你都可以:
  30. Person.setFather
  31. Person.getFather
  32. // 如果你需要联结表两次,你可以联结同一张表
  33. Team.hasOne(Game, {as: 'HomeTeam', foreignKey : 'homeTeamId'});
  34. Team.hasOne(Game, {as: 'AwayTeam', foreignKey : 'awayTeamId'});
  35. Game.belongsTo(Team);

即使它被称为 HasOne 关联,对于大多数1:1关系,你通常需要BelongsTo关联,因为 BelongsTo 将会在 hasOne 将添加到目标的源上添加 foreignKey.

源键

源关键是源模型中的属性,它是指向目标模型的外键属性. 默认情况下,hasOne关系的源键将是源模型的主要属性. 要使用自定义属性,请使用sourceKey参数.

  1. class User extends Model {}
  2. User.init({/* attributes */}, { sequelize, modelName: 'user' })
  3. class Company extends Model {}
  4. Company.init({/* attributes */}, { sequelize, modelName: 'company' });
  5. // 将 companyName 属性添加到 User
  6. // 使用 Company 的 name 属性作为 source 属性
  7. Company.hasOne(User, {foreignKey: 'companyName', sourceKey: 'name'});

HasOne 和 BelongsTo 之间的区别

在Sequelize 1:1关系中可以使用HasOne和BelongsTo进行设置. 它们适用于不同的场景. 让我们用一个例子来研究这个差异.

假设我们有两个表可以链接 PlayerTeam . 让我们定义他们的模型.

  1. class Player extends Model {}
  2. Player.init({/* attributes */}, { sequelize, modelName: 'player' })
  3. class Team extends Model {}
  4. Team.init({/* attributes */}, { sequelize, modelName: 'team' });

当我们连接 Sequelize 中的两个模型时,我们可以将它们称为一对 sourcetarget 模型.像这样

Player 作为 sourceTeam 作为 target

  1. Player.belongsTo(Team);
  2. //或
  3. Player.hasOne(Team);

Team 作为 sourcePlayer 作为 target

  1. Team.belongsTo(Player);
  2. //Or
  3. Team.hasOne(Player);

HasOne 和 BelongsTo 将关联键插入到不同的模型中. HasOne 在 target 模型中插入关联键,而 BelongsTo 将关联键插入到 source 模型中.

下是一个示例,说明了 BelongsTo 和 HasOne 的用法.

  1. class Player extends Model {}
  2. Player.init({/* attributes */}, { sequelize, modelName: 'player' })
  3. class Coach extends Model {}
  4. Coach.init({/* attributes */}, { sequelize, modelName: 'coach' })
  5. class Team extends Model {}
  6. Team.init({/* attributes */}, { sequelize, modelName: 'team' });

假设我们的 Player 模型有关于其团队的信息为 teamId 列. 关于每个团队的 Coach 的信息作为 coachId 列存储在 Team 模型中. 这两种情况都需要不同种类的1:1关系,因为外键关系每次出现在不同的模型上.

当关于关联的信息存在于 source 模型中时,我们可以使用 belongsTo. 在这种情况下,Player 适用于 belongsTo,因为它具有 teamId 列.

  1. Player.belongsTo(Team) // `teamId` 将被添加到 Player / Source 模型中

当关于关联的信息存在于 target 模型中时,我们可以使用 hasOne. 在这种情况下, Coach 适用于 hasOne ,因为 Team 模型将其 Coach 的信息存储为 coachId 字段.

  1. Coach.hasOne(Team) // `coachId` 将被添加到 Team / Target 模型中