2017-01-22 1 views
4

Bei Verwendung der Migrationsfunktion des Dienstprogramms sequelize cli können neue Fremdschlüssel durch Hinzufügen einer neuen Spalte erstellt werden. Beim Versuch, mehrere Fremdschlüssel für dasselbe Modell zu erstellen, tritt jedoch ein Fehler auf. Die Fremdschlüssel-Namen, die durch Ausführen von sequelize db:migrate erzeugt werden, sind nicht unterscheidungskräftig. Der DB-Engine setzt voraus, dass alle Fremdschlüssel sind ein eindeutiger Name zugewiesen, aber SequelizeJS scheinen initally alle Fremdschlüssel zu nennen wie:SequelizeJS-Migration: Hinzufügen mehrerer Fremdschlüssel zum gleichen Modell?

// Excerpt from sequelize/lib/dialects/mysql/query-generator.js 
// Line 195 
{fkName: this.quoteIdentifier(attrName + '_foreign_idx')} 

und dies wird natürlich produzieren gleiche Schlüssel, wenn die Indexnummer idx nicht korrekt erhöht .

Irgendwo in der Sequelize Bibliothek, der idx Teil _foreign_idx muss durch einen tatsächlichen numerischen Wert ersetzt werden, wenn Modelle mit db.sync() Initialisierung, aber ich habe nicht in der Lage gewesen, wo zu identifizieren. Ich habe auch verifiziert, dass Sequelize tatsächlich den Indexwert bei der Verwendung von db.sync() erhöht, indem die Fremdschlüssel in einer anderen Datenbank untersucht werden. In dieser Datenbank wurden die Fremdschlüssel genannt als _ibfk_1, _ibfk_2, .., _ibfk_n

  • Hat jemand ein Problem festgestellt, wo der Fremdschlüsselgenerator identische Namen erzeugt?
  • Hat jemand einen Vorschlag, wie dies bei der Verwendung von Sequelize-Migrationen vermieden/gelindert werden kann?

Ich verwende MySQL als Datenbank-Engine, aber der Fremdschlüssel Name Generator folgt die gleiche Prozedur für zB. auch postgre, soweit ich in der Lage war, den Sequelize-Quellcode zu interpretieren.

Beispiel

Die folgende Migration wird drei Modelle erstellen und die Beziehungen zwischen ihnen mit der Funktion addColumn Funktion von sequelize cli erstellen.

Die Skriptmodelle ein Szenario, in dem sowohl ein Trainer und ein Team von einigen Sponsor gesponsert wird. In diesem Modell möchten wir sowohl in unserem Trainer- als auch in unserem Teammodell auf die Sponsor-ID verweisen. Leider werden dadurch zwei Fremdschlüssel mit dem Namen sponsorId_foreign_idx (einer im Bus und einer im Teammodell) erstellt und die Fremdschlüssel haben keine eindeutigen Namen. Dies würde jedoch vermieden werden, wenn idx um einen inkrementierten Wert geändert würde.

var Promise = require('bluebird'); 

module.exports = { 
    up: function (queryInterface, Sequelize) { 
    return Promise 
     .join(
     queryInterface 
      .createTable('sponsor', { 
      id: { 
       autoIncrement: true, 
       primaryKey: true, 
       type: Sequelize.INTEGER 
      }, 
      name: { 
       type: Sequelize.STRING 
      }, 
      }), 
     queryInterface 
      .createTable('team', { 
      id: { 
       autoIncrement: true, 
       primaryKey: true, 
       type: Sequelize.INTEGER 
      }, 
      name: { 
       type: Sequelize.STRING 
      }, 
      }), 
     queryInterface 
      .createTable('coach', { 
      id: { 
       autoIncrement: true, 
       primaryKey: true, 
       type: Sequelize.INTEGER 
      }, 
      name: { 
       type: Sequelize.STRING 
      }, 
      }) 
    ) 
     .then(function(){ 
     return queryInterface 
      .addColumn('team', 'sponsorId', { 
      type: Sequelize.INTEGER, 
      references: { model: 'sponsor', key: 'id' } 
      }) 
      .then(function(){ 
      return queryInterface 
       .addColumn('coach', 'sponsorId', { 
       type: Sequelize.INTEGER, 
       references: { model: 'sponsor', key: 'id' } 
       }); 
      }); 
     }); 
    }, 

    down: function (queryInterface, Sequelize) { 
    return queryInterface.dropAllTables(); 
    } 
}; 

komplette Fehlerprotokoll Dump

{ SequelizeBaseError: ER_DUP_KEY: Can't write; duplicate key in table '#sql-3b7_f1' 
    at Query.formatError (/home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:175:14) 
    at Query._callback (/home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:49:21) 
    at Query.Sequence.end (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Sequence.js:86:24) 
    at Query.ErrorPacket (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Query.js:94:8) 
    at Protocol._parsePacket (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:280:23) 
    at Parser.write (/home/usr/me/node_modules/mysql/lib/protocol/Parser.js:74:12) 
    at Protocol.write (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:39:16) 
    at Socket.<anonymous> (/home/usr/me/node_modules/mysql/lib/Connection.js:109:28) 
    at emitOne (events.js:96:13) 
    at Socket.emit (events.js:188:7) 
    at readableAddChunk (_stream_readable.js:176:18) 
    at Socket.Readable.push (_stream_readable.js:134:10) 
    at TCP.onread (net.js:551:20) 
    name: 'SequelizeDatabaseError', 
    message: 'ER_DUP_KEY: Can\'t write; duplicate key in table \'#sql-3b7_f1\'', 
    parent: 
    { Error: ER_DUP_KEY: Can't write; duplicate key in table '#sql-3b7_f1' 
     at Query.Sequence._packetToError (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Sequence.js:52:14) 
     at Query.ErrorPacket (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Query.js:83:18) 
     at Protocol._parsePacket (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:280:23) 
     at Parser.write (/home/usr/me/node_modules/mysql/lib/protocol/Parser.js:74:12) 
     at Protocol.write (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:39:16) 
     at Socket.<anonymous> (/home/usr/me/node_modules/mysql/lib/Connection.js:109:28) 
     at emitOne (events.js:96:13) 
     at Socket.emit (events.js:188:7) 
     at readableAddChunk (_stream_readable.js:176:18) 
     at Socket.Readable.push (_stream_readable.js:134:10) 
     at TCP.onread (net.js:551:20) 
     -------------------- 
     at Protocol._enqueue (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:141:48) 
     at Connection.query (/home/usr/me/node_modules/mysql/lib/Connection.js:214:25) 
     at /home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:40:21 
     at Promise._execute (/home/usr/me/node_modules/bluebird/js/release/debuggability.js:300:9) 
     at Promise._resolveFromExecutor (/home/usr/me/node_modules/bluebird/js/release/promise.js:481:18) 
     at new Promise (/home/usr/me/node_modules/bluebird/js/release/promise.js:77:14) 
     at Query.run (/home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:39:17) 
     at /home/usr/me/node_modules/sequelize/lib/sequelize.js:849:20 
     at /home/usr/me/node_modules/retry-as-promised/index.js:40:21 
     at Promise._execute (/home/usr/me/node_modules/bluebird/js/release/debuggability.js:300:9) 
     at Promise._resolveFromExecutor (/home/usr/me/node_modules/bluebird/js/release/promise.js:481:18) 
     at new Promise (/home/usr/me/node_modules/bluebird/js/release/promise.js:77:14) 
     at retryAsPromised (/home/usr/me/node_modules/retry-as-promised/index.js:30:10) 
     at /home/usr/me/node_modules/sequelize/lib/sequelize.js:848:12 
     at tryCatcher (/home/usr/me/node_modules/bluebird/js/release/util.js:16:23) 
     at Promise._settlePromiseFromHandler (/home/usr/me/node_modules/bluebird/js/release/promise.js:510:31) 
    code: 'ER_DUP_KEY', 
    errno: 1022, 
    sqlState: '23000', 
    index: 0, 
    sql: 'ALTER TABLE `coach` ADD `sponsorId` INTEGER, ADD CONSTRAINT `sponsorId_foreign_idx` FOREIGN KEY (`sponsorId`) REFERENCES `sponsor` (`id`);' }, 
    original: 
    { Error: ER_DUP_KEY: Can't write; duplicate key in table '#sql-3b7_f1' 
     at Query.Sequence._packetToError (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Sequence.js:52:14) 
     at Query.ErrorPacket (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Query.js:83:18) 
     at Protocol._parsePacket (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:280:23) 
     at Parser.write (/home/usr/me/node_modules/mysql/lib/protocol/Parser.js:74:12) 
     at Protocol.write (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:39:16) 
     at Socket.<anonymous> (/home/usr/me/node_modules/mysql/lib/Connection.js:109:28) 
     at emitOne (events.js:96:13) 
     at Socket.emit (events.js:188:7) 
     at readableAddChunk (_stream_readable.js:176:18) 
     at Socket.Readable.push (_stream_readable.js:134:10) 
     at TCP.onread (net.js:551:20) 
     -------------------- 
     at Protocol._enqueue (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:141:48) 
     at Connection.query (/home/usr/me/node_modules/mysql/lib/Connection.js:214:25) 
     at /home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:40:21 
     at Promise._execute (/home/usr/me/node_modules/bluebird/js/release/debuggability.js:300:9) 
     at Promise._resolveFromExecutor (/home/usr/me/node_modules/bluebird/js/release/promise.js:481:18) 
     at new Promise (/home/usr/me/node_modules/bluebird/js/release/promise.js:77:14) 
     at Query.run (/home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:39:17) 
     at /home/usr/me/node_modules/sequelize/lib/sequelize.js:849:20 
     at /home/usr/me/node_modules/retry-as-promised/index.js:40:21 
     at Promise._execute (/home/usr/me/node_modules/bluebird/js/release/debuggability.js:300:9) 
     at Promise._resolveFromExecutor (/home/usr/me/node_modules/bluebird/js/release/promise.js:481:18) 
     at new Promise (/home/usr/me/node_modules/bluebird/js/release/promise.js:77:14) 
     at retryAsPromised (/home/usr/me/node_modules/retry-as-promised/index.js:30:10) 
     at /home/usr/me/node_modules/sequelize/lib/sequelize.js:848:12 
     at tryCatcher (/home/usr/me/node_modules/bluebird/js/release/util.js:16:23) 
     at Promise._settlePromiseFromHandler (/home/usr/me/node_modules/bluebird/js/release/promise.js:510:31) 
    code: 'ER_DUP_KEY', 
    errno: 1022, 
    sqlState: '23000', 
    index: 0, 
    sql: 'ALTER TABLE `coach` ADD `sponsorId` INTEGER, ADD CONSTRAINT `sponsorId_foreign_idx` FOREIGN KEY (`sponsorId`) REFERENCES `sponsor` (`id`);' }, 
    sql: 'ALTER TABLE `coach` ADD `sponsorId` INTEGER, ADD CONSTRAINT `sponsorId_foreign_idx` FOREIGN KEY (`sponsorId`) REFERENCES `sponsor` (`id`);' } 
+0

Haben Sie jemals eine geeignete Problemumgehung gefunden? Sieht so aus [war in ihrer v4-Entwicklung behoben] (https://github.com/sequelize/sequelize/pull/6008), aber nicht in v3. –

+0

Ich habe versucht, zugeschnittene SQL-Abfragen zu schreiben, indem ich die Schnittstelle benutze, die bei 'queryInterface.sequelize.query' exponiert ist ([die Existenz wird auf der github-Seite erwähnt]) (https://github.com/sequelize/cli)) – jorgenkg

Antwort

2

Nach einigem Graben fand ich, dass this is a bug in v3.21 eingeführt. Jemand freundlicherweise fixed it in the v4 alpha branch, aber nicht in v3, das ist der aktuelle Release-Zweig. Ich habe eine backport of the patch für v3 eingereicht, aber es wird eine Weile dauern, bis es in ein Release wird.

In der Zwischenzeit habe ich auf manuelle Abfragen zurückgegriffen, wie Sie in Ihrem Kommentar oben empfohlen haben. Es ist nicht zu schmerzhaft, da es nur das Hinzufügen/Ändern von Abfragen für vorhandene Tabellen, aber keine Fremdschlüssel für neue Tabellen betrifft.

Die Beispiele sind ein bisschen in der Dokumentation vage, so dass für andere, die hier über diese Frage stolpern kann das, was ich von einem umzug Migration haben.

up: function (queryInterface, Sequelize) { 
    return queryInterface 
     .addColumn('operators', 'organization_id', { 
      type: Sequelize.INTEGER, 
      allowNull: true, 
      // This bit will cause the naming conflict 
      // references: { model: 'organizations', key: 'id' }, 
      // onUpdate: 'CASCADE', 
      // onDelete: 'RESTRICT' 
     }) 
     .then(() => queryInterface 
      .sequelize 
      .query('ALTER TABLE `operators` ADD CONSTRAINT `operators_organization_id_foreign_idx` FOREIGN KEY (`organization_id`) REFERENCES `organizations` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;', 
       { type: Sequelize.QueryTypes.RAW })); 
}, 
Verwandte Themen