Javascript “问题”;包括「;使用复合外键时使用Sequelize

Javascript “问题”;包括「;使用复合外键时使用Sequelize,javascript,mysql,sql,node.js,sequelize.js,Javascript,Mysql,Sql,Node.js,Sequelize.js,以下是我的主表示例: +-------------+------------------------+--------------------+ | tenant | vas_id | friendly_name | +-------------+------------------------+--------------------+ | brand_1 | 1gb_data_zone1 | 1GB Data in

以下是我的主表示例:

+-------------+------------------------+--------------------+
| 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          |
+-------------+------------------------+--------------------+
tenant
vas_id
是此表中的两个主键(也称为复合主键),它们一起用作另一个表的constain,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
        }
    });
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
        }
    });
…这是我用来将上表与另一个表(vas_定价)关联的代码:

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

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
        }
    }
]

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

在我看来,比起
vas
,你更喜欢
vas\u定价。因此,我建议您在
vas\u 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将出现问题

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

这样Sequelize就可以工作了,我将所有数据保持在数据库级别的一致性

更改概述:

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
vas\u id
仍然是
vas\u 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 |
+---------+------------------------+-----------------+-------------------+-------------------+
我将
租户
作为主键之一删除:

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
        }
    });
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' });
结果:

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);
    });

Sequelize现在在查找正确的子条目方面没有问题,MySQL负责数据一致性,以防止在错误的
tenant
vas_id
密钥对中写入错误的数据。

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