Sequelize中Column specified twice错误及关联表列命名规则咨询
Sequelize多对多关联表列名命名规则及重复列问题解决
问题场景
定义了两张多对多关联的表Images和Categories,关联表为ImageCategories,相关迁移与模型文件如下:
迁移文件
create-image-migration.js
'use strict'; module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.createTable('Images', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, name: { type: Sequelize.STRING }, ... }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('Images'); } };
create-category-migration.js
'use strict'; module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.createTable('Categories', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, name: { type: Sequelize.STRING }, ... }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('Categories'); } };
create-image-category-migration.js
'use strict'; module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.createTable('ImageCategories', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, imageId: { type: Sequelize.INTEGER, allowNull: false, references: { model: 'Images', key: 'id' } }, categoryId: { type: Sequelize.INTEGER, allowNull: false, references: { model: 'Categories', key: 'id' } }, ... }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('ImageCategories'); } };
关联表模型文件 image-category-model.js
'use strict'; module.exports = (sequelize, DataTypes) => { const ImageCategory = sequelize.define('ImageCategory', { imageId: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'Image', key: 'id' }, }, categoryId: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'Category', key: 'id' }, }, ... }, {}); ImageCategory.associate = function(models) { models.Image.belongsToMany(models.Category, { through: ImageCategory }); models.Category.belongsToMany(models.Image, { through: ImageCategory }); }; return ImageCategory; };
执行bulkCreate插入数据时:
await db.ImageCategory.bulkCreate([ { imageId: 'someId', categoryId: topicId, categoryType: 'topic' }, { imageId: 'someId', categoryId: styleId, categoryType: 'style' }, ]);
出现错误:
sqlMessage: "Column 'imageId' specified twice", sql: "INSERT INTO `ImageCategories` (`imageId`,`categoryId`,`categoryType`,`createdAt`,`updatedAt`,`ImageId`) VALUES (5,'22','topic','2022-11-26 08:11:41','2022-11-26 08:11:41',NULL),(5,'27','style','2022-11-26 08:11:41','2022-11-26 08:11:41',NULL);"
可见Sequelize自动添加了ImageId列,导致列名重复。
核心原因
未在belongsToMany关联配置中手动指定外键列名,Sequelize根据默认规则生成了首字母大写的ImageId/CategoryId列,同时模型中又手动定义了小写开头的imageId/categoryId列,插入时两者冲突。
Sequelize关联表列名命名规则
- 默认生成规则:使用
belongsToMany建立多对多关联时,若不指定外键,Sequelize会自动生成外键列名,格式为关联模型名首字母大写 + Id(例如关联Image模型生成ImageId,关联Category模型生成CategoryId)。 - 手动指定规则:可在
belongsToMany的配置选项中,通过foreignKey(当前模型对应的外键)和otherKey(关联模型对应的外键)明确指定关联表中的列名,覆盖默认规则。 - 一致性要求:关联表的模型定义、迁移文件中的列名必须完全一致(包括大小写,需注意数据库是否区分大小写,如MySQL默认不区分,但Sequelize字段名区分)。
解决办法
修改关联表模型的associate方法,手动指定外键列名,与迁移文件、模型定义保持一致:
ImageCategory.associate = function(models) { models.Image.belongsToMany(models.Category, { through: ImageCategory, foreignKey: 'imageId', // 指定Image对应的外键为imageId otherKey: 'categoryId' // 指定Category对应的外键为categoryId }); models.Category.belongsToMany(models.Image, { through: ImageCategory, foreignKey: 'categoryId', // Category关联Image时,自身外键为categoryId otherKey: 'imageId' // 关联Image的外键为imageId }); };
内容的提问来源于stack exchange,提问作者DrusePstrago
相关产品推荐
相关产品推荐

