zoukankan      html  css  js  c++  java
  • Sequelize数据库关联

      本博客描述 Sequelize 中的各种关联类型。当调用诸如 User.hasOne(Project) 之类的方法时,我们说 User 模型(函数被调用的模型)是 sourceProject 模型(作为参数传递的模型)是 target

    一、一对一关联

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

    1、BelongsTo:源model - 属于的意思

      属于的意思:源属于目标,在源上添加目标外键

      BelongsTo 关联是在 source model 上存在一对一关系的外键的关联。一个简单的例子是 Player 通过 player 的外键作为 Team 的一部分。

    const Player = this.sequelize.define('player', {/* attributes */});
    const Team  = this.sequelize.define('team', {/* attributes */});
    
    Player.belongsTo(Team); // 将向 Player 添加一个 teamId 属性以保存 Team 的主键值

    2、外键

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

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

    const User = this.sequelize.define('user', {/* attributes */})
    const Company  = this.sequelize.define('company', {/* attributes */});
    
    User.belongsTo(Company); // 将 companyId 添加到 user
    
    const User = this.sequelize.define('user', {/* attributes */}, {underscored: true})
    const Company  = this.sequelize.define('company', {
      uuid: {
        type: Sequelize.UUID,
        primaryKey: true
      }
    });
    
    User.belongsTo(Company); // 将 company_uuid 添加到 user

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

    const User = this.sequelize.define('user', {/* attributes */})
    const UserRole  = this.sequelize.define('userRole', {/* attributes */});
    
    User.belongsTo(UserRole, {as: 'role'}); // 将 role 添加到 user 而不是 userRole

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

    const User = this.sequelize.define('user', {/* attributes */})
    const Company  = this.sequelize.define('company', {/* attributes */});
    
    User.belongsTo(Company, {foreignKey: 'fk_company'}); // 将 fk_company 添加到 User

    3、目标键

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

    const User = this.sequelize.define('user', {/* attributes */})
    const Company  = this.sequelize.define('company', {/* attributes */});
    
    User.belongsTo(Company, {foreignKey: 'fk_companyname', targetKey: 'name'}); // 添加 fk_companyname 到 User

    4、HasOne:目标model

      有一个的意思,源有一个目标,在目标上添加源的外键

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

    const User = sequelize.define('user', {/* ... */})
    const Project = sequelize.define('project', {/* ... */})
     
    // 单向关联
    Project.hasOne(User)
    
    /*
      在此示例中,hasOne 将向 User 模型添加一个 projectId 属性 ! 
      此外,Project.prototype 将根据传递给定义的第一个参数获取 getUser 和 setUser 的方法。 
      如果启用了 underscore 样式,则添加的属性将是 project_id 而不是 projectId。
    
      外键将放在 users 表上。
    
      你也可以定义外键,例如 如果您已经有一个现有的数据库并且想要处理它:
    */
     
    Project.hasOne(User, { foreignKey: 'initiator_id' })
     
    /*
      因为Sequelize将使用模型的名称(define的第一个参数)作为访问器方法,
      还可以将特殊选项传递给hasOne:
    */
     
    Project.hasOne(User, { as: 'Initiator' })
    // 现在你可以获得 Project.getInitiator 和 Project.setInitiator
     
    // 或者让我们来定义一些自己的参考
    const Person = sequelize.define('person', { /* ... */})
     
    Person.hasOne(Person, {as: 'Father'})
    // 这会将属性 FatherId 添加到 Person
     
    // also possible:
    Person.hasOne(Person, {as: 'Father', foreignKey: 'DadId'})
    // 这将把属性 DadId 添加到 Person
     
    // 在这两种情况下,你都可以:
    Person.setFather
    Person.getFather
     
    // 如果你需要联结表两次,你可以联结同一张表
    Team.hasOne(Game, {as: 'HomeTeam', foreignKey : 'homeTeamId'});
    Team.hasOne(Game, {as: 'AwayTeam', foreignKey : 'awayTeamId'});
    
    Game.belongsTo(Team);

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

    5、HasOne 和 BelongsTo 之间的区别

      在Sequelize 1:1关系中可以使用HasOne和BelongsTo进行设置。 它们适用于不同的场景。 让我们用一个例子来研究这个差异。假设我们有两个表可以链接 PlayerTeam 。 让我们定义他们的模型。

    const Player = this.sequelize.define('player', {/* attributes */})
    const Team  = this.sequelize.define('team', {/* attributes */});

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

      将 Player 作为 sourceTeam 作为 target

    Player.belongsTo(Team);
    //
    Player.hasOne(Team);

      将 Team 作为 sourcePlayer 作为 target

    Team.belongsTo(Player);
    //Or
    Team.hasOne(Player);

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

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

    const Player = this.sequelize.define('player', {/* attributes */})
    const Coach  = this.sequelize.define('coach', {/* attributes */})
    const Team  = this.sequelize.define('team', {/* attributes */});

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

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

    Player.belongsTo(Team)  // `teamId` 将被添加到 Player / Source 模型中
      当关于关联的信息存在于 target 模型中时,我们可以使用 hasOne。 在这种情况下, Coach 适用于 hasOne ,因为 Team 模型将其 Coach 的信息存储为 coachId 字段。
    Coach.hasOne(Team)  // `coachId` 将被添加到 Team / Target 模型中

    二、一对多关联 (hasMany)

      一对多关联将一个来源与多个目标连接起来。 而多个目标接到同一个特定的源。

    const User = sequelize.define('user', {/* ... */})
    const Project = sequelize.define('project', {/* ... */})
     
    // 好。 现在,事情变得更加复杂(对用户来说并不真实可见)。
    // 首先我们来定义一个 hasMany 关联
    Project.hasMany(User, {as: 'Workers'})

      这会将 projectIdproject_id 属性添加到 User。Project 的实例将获得访问器 getWorkerssetWorkers

      有时您可能需要在不同的列上关联记录,您可以使用 sourceKey 选项:

    const City = sequelize.define('city', { countryCode: Sequelize.STRING });
    const Country = sequelize.define('country', { isoCode: Sequelize.STRING });
    
    // 在这里,我们可以根据国家代码连接国家和城市
    Country.hasMany(City, {foreignKey: 'countryCode', sourceKey: 'isoCode'});
    City.belongsTo(Country, {foreignKey: 'countryCode', targetKey: 'isoCode'});

      到目前为止,我们解决了单向关联。 但我们想要更多! 让我们通过在下一节中创建一个多对多的关联来定义它。

    三、多对多关联

      多对多关联用于将源与多个目标相连接。 此外,目标也可以连接到多个源。

    Project.belongsToMany(User, {through: 'UserProject'});
    User.belongsToMany(Project, {through: 'UserProject'});

      这将创建一个名为 UserProject 的新模型,具有等效的外键projectIduserId。 属性是否为camelcase取决于由表(在这种情况下为UserProject)连接的两个模型。

      定义 throughrequired。 Sequelize 以前会尝试自动生成名称,但并不总是导致最合乎逻辑的设置。

      这将添加方法 getUsers, setUsers, addUser,addUsersProject, 还有 getProjects, setProjects, addProject, 和 addProjectsUser

      有时,您可能需要在关联中使用它们时重命名模型。 让我们通过使用别名(as)选项将 users 定义为 workers 而 projects 定义为t asks。 我们还将手动定义要使用的外键:

    User.belongsToMany(Project, { as: 'Tasks', through: 'worker_tasks', foreignKey: 'userId' })
    Project.belongsToMany(User, { as: 'Workers', through: 'worker_tasks', foreignKey: 'projectId' })

      foreignKey 将允许你在 through 关系中设置 source model 键。

      otherKey 将允许你在 through 关系中设置 target model 键。

    User.belongsToMany(Project, { as: 'Tasks', through: 'worker_tasks', foreignKey: 'userId', otherKey: 'projectId'})

      当然你也可以使用 belongsToMany 定义自我引用:

    Person.belongsToMany(Person, { as: 'Children', through: 'PersonChildren' })
    // 这将创建存储对象的 ID 的表 PersonChildren。

      如果您想要连接表中的其他属性,则可以在定义关联之前为连接表定义一个模型,然后再说明它应该使用该模型进行连接,而不是创建一个新的关联:

    const User = sequelize.define('user', {})
    const Project = sequelize.define('project', {})
    const UserProjects = sequelize.define('userProjects', {
        status: DataTypes.STRING
    })
     
    User.belongsToMany(Project, { through: UserProjects })
    Project.belongsToMany(User, { through: UserProjects })

      要向 user 添加一个新 project 并设置其状态,您可以将额外的 options.through 传递给 setter,其中包含连接表的属性

    user.addProject(project, { through: { status: 'started' }})
      默认情况下,上面的代码会将 projectId 和 userId 添加到 UserProjects 表中, 删除任何先前定义的主键属性 - 表将由两个表的键的组合唯一标识,并且没有其他主键列。 要在 UserProjects 模型上强添加一个主键,您可以手动添加它。
    const UserProjects = sequelize.define('userProjects', {
      id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
      },
      status: DataTypes.STRING
    })

      使用多对多你可以基于 through 关系查询并选择特定属性。 例如通过 through 使用findAll

    User.findAll({
      include: [{
        model: Project,
        through: {
          attributes: ['createdAt', 'startedAt', 'finishedAt'],
          where: {completed: true}
        }
      }]
    });

      当通过模型不存在主键时,Belongs-To-Many会创建唯一键。 可以使用 uniqueKey 选项覆盖此唯一键名。

    Project.belongsToMany(User, { through: UserProjects, uniqueKey: 'my_custom_unique' })
  • 相关阅读:
    hdu5081
    hdu5079
    hdu5076
    hdu5072
    codeforces 739E
    codeforces 739D
    2017.2其他简要题解
    spring事务传播回滚策略
    mybatis一级缓存和二级缓存
    spring-boot-mybatis-starter工作原理
  • 原文地址:https://www.cnblogs.com/goloving/p/13471530.html
Copyright © 2011-2022 走看看