使用自定义连接表主键 Sequelize BelongsToMany

Posted

技术标签:

【中文标题】使用自定义连接表主键 Sequelize BelongsToMany【英文标题】:Sequelize BelongsToMany with custom join table primary key 【发布时间】:2017-05-22 13:44:15 【问题描述】:

我有一个多对多的关系,中间有一个连接表。这些表是 Cookoff、Participant 和 CookoffParticipant。我应该提到我不允许 Sequelize 创建或修改我的表,我只是在映射我现有的关系。我需要帮助了解哪些关系选项告诉 sequelize 调用将连接表与主表相关联的外键。

据我了解,Sequelize 假定 CookoffID 和 ParticipantID 是 CookoffParticipant 上的复合主键。在我的情况下,我要求主键是我调用 CookoffParticipantID 的标识列,并在 CookoffParticipant 表中的 CookoffID、ParticipantID 对上创建唯一索引。

当我尝试通过查询 cookoffParticipant 表来获取烹饪和参与者数据时,Sequelize 使用了错误的键来完成连接。一定有一些简单的事情我没有做。下面是我的表结构和带有结果的查询。

烹饪台

var Cookoff = sequelize.define("Cookoff", 

    // Table columns

    CookoffID: 
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true
    ,
    Title: 
        type: DataTypes.STRING,
        allowNull: false
    ,
    EventDate: 
        type: DataTypes.DATE,
        allowNull: false
    
, _.extend(,

    // Table settings
    defaultTableSettings,

    
        classMethods: 
            associate: function(models) 
                Cookoff.belongsToMany(models.Participant, 
                    through: 
                        model: models.CookoffParticipant
                    ,
                    as: "Cookoffs",
                    foreignKey: "CookoffID",
                    otherKey: "ParticipantID"
                );
            
        
    
));

参与者表

var Participant = sequelize.define("Participant", 

    // Table columns
    ParticipantID: 
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true
    ,
    Name: 
        type: DataTypes.STRING(100),
        allowNull: false
    

, _.extend(,

    defaultTableSettings,

    
        classMethods: 
            associate: function(models) 
                Participant.belongsToMany(models.Cookoff, 
                    through: 
                        model: models.CookoffParticipant
                    ,
                    as: "Participants",
                    foreignKey: "ParticipantID",
                    otherKey: "CookoffID"
                );
            
        
    
));

Cookoff 参与者表

var CookoffParticipant = sequelize.define("CookoffParticipant", 
    CookoffParticipantID: 
        type: DataTypes.INTEGER,
        allowNull: false,
        primaryKey: true,
        autoIncrement: true
    ,
    CookoffID: 
        type: DataTypes.INTEGER,
        allowNull: false,
        references: 
            model: cookoff,
            key: "CookoffID"
        
    ,
    ParticipantID: 
        type: DataTypes.INTEGER,
        allowNull: false,
        references: 
            model: participant,
            key: "ParticipantID"
        
    
, _.extend(
     ,
    defaultTableSettings,
    
        classMethods: 
          associate: function (models) 
              CookoffParticipant.hasOne(models.Cookoff,  foreignKey: "CookoffID" );
              CookoffParticipant.hasOne(models.Participant,  foreignKey: "ParticipantID" );

            
        
    
));

我的查询

return cookoffParticpants.findOne(
        where:  CookoffID: cookoffID, ParticipantID: participantID ,
        include: [
             model: participants ,
             model: cookoffs 
        ]
    );

生成的 SQL

SELECT 
    [CookoffParticipant].[CookoffParticipantID], 
    [CookoffParticipant].[CookoffID], 
    [CookoffParticipant].[ParticipantID], 
    [Participant].[ParticipantID] AS [Participant.ParticipantID], 
    [Participant].[Name] AS [Participant.Name], 
    [Cookoff].[CookoffID] AS [Cookoff.CookoffID], 
    [Cookoff].[Title] AS [Cookoff.Title], 
    [Cookoff].[EventDate] AS [Cookoff.EventDate] 
FROM [CookoffParticipant] AS [CookoffParticipant] 
LEFT OUTER JOIN [Participant] AS [Participant] 
    ON [CookoffParticipant].[CookoffParticipantID] = [Participant].[ParticipantID]  -- This should be CookoffParticipant.ParticipantID
LEFT OUTER JOIN [Cookoff] AS [Cookoff] 
    ON [CookoffParticipant].[CookoffParticipantID] = [Cookoff].[CookoffID] -- This should be CookoffParticipant.CookoffID
WHERE [CookoffParticipant].[CookoffID] = 1 
AND [CookoffParticipant].[ParticipantID] = 6 
ORDER BY [CookoffParticipantID] 
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY;

您可以看到 Sequelize 正在尝试在 Participant.ParticipantID 上加入 CookoffParticipant.CookoffParticipantID,其中应该是 CookoffParticipant.ParticipantID = Participant.ParticipantID 和 CookoffID 类似。我在这里做错了什么?

提前感谢您的帮助。

【问题讨论】:

【参考方案1】:

这是您正在寻找的非常好的discussion。他们很好地总结了这一点,说您应该同时定义一个直通表和声明belongsTo 引用的直通表。您的问题可能是您使用了hasOne 而不是belongsTo。另外我认为你的as 键是向后的。

Cookoff.hasMany(Book,  through: CookoffParticipant )
Participant.hasMany(User,  through: CookoffParticipant )
CookoffParticipant.belongsTo(Cookoff)
CookoffParticipant.belongsTo(Participant)

这是我用来测试的代码。

Cookoff.js

module.exports = (sequelize, DataTypes) => 
    var Cookoff = sequelize.define("Cookoff", 
        CookoffID: 
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
        
    , _.extend(
        ,
        
            classMethods: 
                associate: function(models) 
                    Cookoff.belongsToMany(models.Participant, 
                        through: models.CookoffParticipant,
                        foreignKey: "CookoffID",
                        otherKey: "ParticipantID"
                    );
                
            
        
    ));
    return Cookoff;
;

参与者.js

module.exports = (sequelize, DataTypes) => 
    var Participant = sequelize.define("Participant", 
        ParticipantID: 
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
        
    , _.extend(
        ,
        
            classMethods: 
                associate: function(models) 
                    Participant.belongsToMany(models.Cookoff, 
                        through: models.CookoffParticipant,
                        foreignKey: "ParticipantID",
                        otherKey: "CookoffID"
                    );
                
            
        
    ));
    return Participant;
;

CookoffParticipant.js

module.exports = (sequelize, DataTypes) => 
    var CookoffParticipant = sequelize.define("CookoffParticipant", 
        CookoffParticipantID: 
            type: DataTypes.INTEGER,
            allowNull: false,
            primaryKey: true,
            autoIncrement: true
        
    , _.extend(
        ,
        
            classMethods: 
                associate: function(models) 
                    CookoffParticipant.belongsTo(models.Cookoff,  foreignKey: "CookoffID" );
                    CookoffParticipant.belongsTo(models.Participant,  foreignKey: "ParticipantID" );
                
            
        
    ));
    return CookoffParticipant;
;

test.js

const db = require('../db');
const Cookoff = db.Cookoff;
const Participant = db.Participant;
const CookoffParticipant = db.CookoffParticipant;
let cookoff,
    participant;

Promise.all([
    Cookoff.create(),
    Participant.create()
]).then(([ _cookoff, _participant ]) => 
    cookoff = _cookoff;
    participant = _participant;

    return cookoff.addParticipant(participant);
).then(() => 
    return CookoffParticipant.findOne(
        where:  CookoffID: cookoff.CookoffID, ParticipantID: participant.ParticipantID ,
        include: [ Cookoff, Participant ]
    );
).then(cookoffParticipant => 
    console.log(cookoffParticipant.toJSON());
);

【讨论】:

谢谢。关键错误是使用 hasOne 而不是 belongsTo。进行该开关立即解决了问题。

以上是关于使用自定义连接表主键 Sequelize BelongsToMany的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 主表主键自增量,如何获取主键呢?

mysql表主键从给定值开始自动增长是怎么回事?

mysql表主键从给定值开始自动增长是怎么回事?

SQL 一个表只有一个自增的主键字段,如何插入

Graphql 自定义连接

sql 获取当前插入的主键 表主键是guid类型的