約束條件與循環參照
當使用 sequelize.sync
時,在資料表之間加入約束條件意味著資料表必須依照特定順序在資料庫中建立。如果 Task
參照了 User
,則必須先建立 User
資料表,才能建立 Task
資料表。這有時會導致循環參照,Sequelize 無法找到同步的順序。想像一個文件和版本的場景。一個文件可以有多個版本,為了方便起見,文件會參照其目前版本。
const { Sequelize, Model, DataTypes } = require('sequelize');
class Document extends Model {}
Document.init(
{
author: DataTypes.STRING,
},
{ sequelize, modelName: 'document' },
);
class Version extends Model {}
Version.init(
{
timestamp: DataTypes.DATE,
},
{ sequelize, modelName: 'version' },
);
Document.hasMany(Version); // This adds documentId attribute to version
Document.belongsTo(Version, {
as: 'Current',
foreignKey: 'currentVersionId',
}); // This adds currentVersionId attribute to document
然而,不幸的是,上述程式碼會導致以下錯誤
Cyclic dependency found. documents is dependent of itself. Dependency chain: documents -> versions => documents
為了緩解這種情況,我們可以將 constraints: false
傳遞給其中一個關聯
Document.hasMany(Version);
Document.belongsTo(Version, {
as: 'Current',
foreignKey: 'currentVersionId',
constraints: false,
});
這將允許我們正確同步資料表
CREATE TABLE IF NOT EXISTS "documents" (
"id" SERIAL,
"author" VARCHAR(255),
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"currentVersionId" INTEGER,
PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "versions" (
"id" SERIAL,
"timestamp" TIMESTAMP WITH TIME ZONE,
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"documentId" INTEGER REFERENCES "documents" ("id") ON DELETE
SET
NULL ON UPDATE CASCADE,
PRIMARY KEY ("id")
);
在沒有約束的情況下強制執行外部索引鍵參照
有時您可能想要參照另一個資料表,而無需新增任何約束或關聯。在這種情況下,您可以手動將參照屬性新增至您的結構定義,並標記它們之間的關係。
class Trainer extends Model {}
Trainer.init(
{
firstName: Sequelize.STRING,
lastName: Sequelize.STRING,
},
{ sequelize, modelName: 'trainer' },
);
// Series will have a trainerId = Trainer.id foreign reference key
// after we call Trainer.hasMany(series)
class Series extends Model {}
Series.init(
{
title: Sequelize.STRING,
subTitle: Sequelize.STRING,
description: Sequelize.TEXT,
// Set FK relationship (hasMany) with `Trainer`
trainerId: {
type: DataTypes.INTEGER,
references: {
model: Trainer,
key: 'id',
},
},
},
{ sequelize, modelName: 'series' },
);
// Video will have seriesId = Series.id foreign reference key
// after we call Series.hasOne(Video)
class Video extends Model {}
Video.init(
{
title: Sequelize.STRING,
sequence: Sequelize.INTEGER,
description: Sequelize.TEXT,
// set relationship (hasOne) with `Series`
seriesId: {
type: DataTypes.INTEGER,
references: {
model: Series, // Can be both a string representing the table name or a Sequelize model
key: 'id',
},
},
},
{ sequelize, modelName: 'video' },
);
Series.hasOne(Video);
Trainer.hasMany(Series);