跳至主要内容
版本:v6 - 稳定版

约束和循环引用

在使用 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);