【问题标题】:Problems with "includes" using Sequelize when a composite foreign key is used使用复合外键时使用 Sequelize 的“包含”问题
【发布时间】:2020-02-19 02:35:03
【问题描述】:

这是我的主表的示例:

+-------------+------------------------+--------------------+
| tenant      | vas_id                 | friendly_name      |
+-------------+------------------------+--------------------+
| brand_1     | 1gb_data_zone1         | 1GB Data in Zone 1 |
| brand_1     | promo_summer_2019_10GB | 10GB for Summer    |
| brand_1     | roaming_prepaid        | Roaming            |
| brand_1     | voicemail_prepaid      | Voicemail          |
| brand_2     | test_vas               | Test               |
| brand_2     | roaming_prepaid        | Roaming            |
| brand_2     | voicemail_prepaid      | Voicemail          |
+-------------+------------------------+--------------------+

tenantvas_id是该表中的两个主键(也称为复合主键),它们一起用作对另一个表的约束,1:N关系:

+---------+------------------------+-----------------+-------------------+-------------------+
| tenant  | vas_id                 | activation_cost | deactivation_cost | modification_cost |
+---------+------------------------+-----------------+-------------------+-------------------+
| brand_1 | 1gb_data_zone1         |            2000 |                 0 |                 0 |
| brand_1 | promo_summer_2019_10GB |               0 |                 0 |                 0 |
| brand_1 | roaming_prepaid        |               0 |                 0 |                 0 |
| brand_1 | voicemail_prepaid      |               0 |                 0 |                 0 |
| brand_2 | test_vas               |               0 |                 0 |                 0 |
| brand_2 | roaming_prepaid        |               0 |                 0 |                 0 |
| brand_2 | voicemail_prepaid      |               0 |                 0 |                 0 |
+---------+------------------------+-----------------+-------------------+-------------------

你说这个结构可以和Sequelize相处吗?

这是我用来标记两个主键的代码:

const vas = serviceLayerDB.define('vas',
    { // Database columns:
        tenant: {
            type: Sequelize.STRING(45),
            primaryKey: true
        },
        vas_id: {
            type: Sequelize.STRING(100),
            primaryKey: true
        }
        friendly_name: {
            type: Sequelize.STRING(100)
        }
    }

const vas_pricing = serviceLayerDB.define('vas_pricing',
    { // Database columns:
        tenant: {
            type: Sequelize.STRING(45),
            primaryKey: true
        },
        vas_id: {
            type: Sequelize.STRING(100),
            primaryKey: true
        },
        activation_cost: {
            type: Sequelize.NUMBER
        },
        deactivation_cost: {
            type: Sequelize.NUMBER
        },
        modification_cost: {
            type: Sequelize.NUMBER
        }
    });

...这是我用来将上面的表格与另一个表格(vas_pricing)相关联的代码:

vas.hasOne(vas_pricing, { foreignKey: 'vas_id' });
vas.hasOne(vas_pricing, { foreignKey: 'tenant' });

奇怪的事情发生了,例如,在执行以下在主表和子表中找到的代码时:

let options = {
    where: {
        tenant: 'brand_1',
        vas_id: 'promo_summer_2019_10GB'
    },
    include: [
        {
            model: vas_pricing,
            required: false
        }
    ]
};
vas.findAll(options)
    .then(function(data) {
        console.log(JSON.stringify(data, null, 2))
    })
    .catch(function(error) {
        console.error(error);
    });

结果:

[
    {
        "tenant": "brand_1",
        "vas_id": "promo_summer_2019_10GB",
        "friendly_name": "10GB During Summer",
        "vas_pricing": {
            "tenant": "brand_1",
            "vas_id": "1gb_data_zone1",
            "activation_cost": 20,
            "deactivation_cost": 0,
            "modification_cost": 0
        }
    },
    {
        "tenant": "brand_1",
        "vas_id": "promo_summer_2019_10GB",
        "friendly_name": "10GB During Summer",
        "vas_pricing": {
            "tenant": "brand_1",
            "vas_id": "promo_summer_2019_10GB",
            "activation_cost": 0,
            "deactivation_cost": 0,
            "modification_cost": 0
        }
    },
    {
        "tenant": "brand_1",
        "vas_id": "promo_summer_2019_10GB",
        "friendly_name": "10GB During Summer",
        "vas_pricing": {
            "tenant": "brand_1",
            "vas_id": "roaming_prepaid",
            "activation_cost": 0,
            "deactivation_cost": 0,
            "modification_cost": 0
        }
    },
    {
        "tenant": "brand_1",
        "vas_id": "promo_summer_2019_10GB",
        "friendly_name": "10GB During Summer",
        "vas_pricing": {
            "tenant": "brand_1",
            "vas_id": "voicemail_prepaid",
            "activation_cost": 0,
            "deactivation_cost": 0,
            "modification_cost": 0
        }
    }
]

预期结果:

[
    {
        "tenant": "brand_1",
        "vas_id": "promo_summer_2019_10GB",
        "friendly_name": "10GB During Summer",
        "vas_pricing": {
            "tenant": "brand_1",
            "vas_id": "promo_summer_2019_10GB",
            "activation_cost": 0,
            "deactivation_cost": 0,
            "modification_cost": 0
        }
    }
]

几天来我一直在努力寻找解决方案,但没有成功。有什么想法吗?

【问题讨论】:

    标签: javascript mysql sql node.js sequelize.js


    【解决方案1】:

    在我看来,您更多的是寻找vas_pricing 而不是vas。所以我建议您改为在vas_pricing 上进行查询:

    let options = {
      where: {
        tenant: 'brand_1',
        vas_id: 'promo_summer_2019_10GB'
      },
      include: [
        {
          model: vas,
          required: false
        }
      ]
    };
    
    vas_pricing.findAll(options)
    .then(function(data) {
        console.log(JSON.stringify(data, null, 2))
    })
    .catch(function(error) {
        console.error(error);
    });
    

    【讨论】:

    • 我错误地编辑了您的消息。我试图编辑我自己的。请有人回复。
    • 你好。不是我想要达到的目标,但非常感谢您的建议。我已经分享了最终的解决方案。由于 Sequelize 不支持复合外键,似乎没有什么可以做的。
    【解决方案2】:

    所以经过大量研究,我得出结论,当同时使用两个外键并尝试在子模型中查找时,Sequelize 会出现问题。

    我的解决方法是保留我在数据库中的结构,并将第二个键设置为唯一并告诉 Sequelize 它是唯一的主键和外键,即使不是。

    这样 Sequelize 可以工作,并且我在数据库级别保持所有数据的一致性。

    变更概览:

    tenantvas_id 仍然是 vas_pricing 表的主键和复合外键。但是vas_id 现在是独一无二的:

    +-------------+------------------------+--------------------+
    | tenant      | vas_id (unique)        | friendly_name      |
    +-------------+------------------------+--------------------+
    | brand_1     | 1gb_data_zone1         | 1GB Data in Zone 1 |
    | brand_1     | promo_summer_2019_10GB | 10GB for Summer    |
    | brand_1     | roaming_prepaid_b1     | Roaming            |
    | brand_1     | voicemail_prepaid_b1   | Voicemail          |
    | brand_2     | test_vas               | Test               |
    | brand_2     | roaming_prepaid_b2     | Roaming            |
    | brand_2     | voicemail_prepaid_b2   | Voicemail          |
    +-------------+------------------------+--------------------+
    

    vas_pricing 表中,一切都保持原样:

    +---------+------------------------+-----------------+-------------------+-------------------+
    | tenant  | vas_id                 | activation_cost | deactivation_cost | modification_cost |
    +---------+------------------------+-----------------+-------------------+-------------------+
    | brand_1 | 1gb_data_zone1         |            2000 |                 0 |                 0 |
    | brand_1 | promo_summer_2019_10GB |               0 |                 0 |                 0 |
    | brand_1 | roaming_prepaid        |               0 |                 0 |                 0 |
    | brand_1 | voicemail_prepaid      |               0 |                 0 |                 0 |
    | brand_2 | test_vas               |               0 |                 0 |                 0 |
    | brand_2 | roaming_prepaid        |               0 |                 0 |                 0 |
    | brand_2 | voicemail_prepaid      |               0 |                 0 |                 0 |
    +---------+------------------------+-----------------+-------------------+-------------------+
    

    我删除了 tenant 作为主键之一:

    const vas = serviceLayerDB.define('vas',
        { // Database columns:
            tenant: {
                type: Sequelize.STRING(45),
                // primaryKey: true - NOT ANYMORE
            },
            vas_id: {
                type: Sequelize.STRING(100),
                primaryKey: true
            }
            friendly_name: {
                type: Sequelize.STRING(100)
            }
        }
    
    const vas_pricing = serviceLayerDB.define('vas_pricing',
        { // Database columns:
            tenant: {
                type: Sequelize.STRING(45),
                // primaryKey: true - NOT ANYMORE
            },
            vas_id: {
                type: Sequelize.STRING(100),
                primaryKey: true
            },
            activation_cost: {
                type: Sequelize.NUMBER
            },
            deactivation_cost: {
                type: Sequelize.NUMBER
            },
            modification_cost: {
                type: Sequelize.NUMBER
            }
        });
    

    ...Sequelize 现在将 vas_id 视为唯一的主键:

    vas.hasOne(vas_pricing, { foreignKey: 'vas_id' });
    

    结果:

    Sequelize 现在可以毫无问题地找到正确的子条目,并且 MySQL 负责数据一致性以防止将错误数据写入错误的 tenantvas_id 密钥对。

    【讨论】:

      猜你喜欢
      • 2021-02-01
      • 1970-01-01
      • 2015-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多