Sequelize ORM中HasOne和BelongsTo的区别

Posted

技术标签:

【中文标题】Sequelize ORM中HasOne和BelongsTo的区别【英文标题】:Difference between HasOne and BelongsTo in Sequelize ORM 【发布时间】:2016-04-06 13:02:24 【问题描述】:

我正在使用sequelize ORM 开发一个sails.js 应用程序。我对何时需要使用 BelongsTo 和 HasOne 感到有些困惑。

文档指出:

BelongsTo 关联是关联的外键 源模型上存在一对一关系。

HasOne 关联是其中的外键的关联 目标模型上存在一对一关系。

除了指定这些的地方还有其他区别吗?在这两种情况下,行为是否仍然保持不变?

【问题讨论】:

【参考方案1】:

我知道这是一个迟了 4 年的答案,但我从昨天开始就一直在思考、搜索文档和谷歌搜索。并且找不到让我相信正在发生的事情的答案。今天我得出一个结论:区别绝对只是语义问题!

假设您有以下语句 (from the docs):

Project.hasMany(Task);

它在Project 模型中为Project 的实例创建了一些实用方法,例如:addTasksetTask 等。因此您可以执行以下操作:

const project = await Project.create(...);

// Here, addTask exists in project instance as a 
// consequence of Project.hasMany(Task); statement 
project.addTasks([task1, task2]);

另外,在数据库中,tasks 关系中的外键将被创建,指向projects 关系。

现在,如果不是Project.hasMany(Task);,我只说:

Task.belongsTo(Project);

然后,类似地,在数据库中,tasks 关系中的外键将被创建,指向projects 关系。但是project 实例上不会有任何addTasks 方法。但是,通过执行Task.belongsTo(Project);,Sequelize 将创建一组不同的方法,但这次仅在 task 实例上。之后,您可以使用以下方法将任务与项目相关联:

const proj = await Project.findByPk(...);
const task1 = await Task.create(...);

...

// Here, setProject exists in task instance as a 
// consequence of Task.belongsTo(Project); statement 
task1.setProject(proj);

文档定义为 source,即拥有用于创建关联的方法的模型。所以,在:

Project.hasMany(Task);:在此声明中,Project来源 模型。 Task 又是 target 模型。 Task.belongsTo(Project);:在此声明中,Task来源 模型。 Project 又是 target 模型。

问题是,当使用hasOnehasManybelongsTobelongsToMany 创建关联时,实例实用程序方法仅在 模型。总之:如果您想在Project Task 实例中创建实用程序方法,则必须使用这两个语句来描述相同的关联。在数据库本身中,两者都会产生相同的冗余效果(在tasks 关系上创建一个外键,指向projects 关系的主键):

// All the instances of Project model will have utility methods
Project.hasMany(Task);

// All the instances of Task model will have utility methods
Task.belongsTo(Project);

const project = await Project.create(...);
const task1 = await Task.create(...);
const task2 = await Task.create(...);

...

// as a consequence of Project.hasMany(Task), this can be done:
project.addTask(task1);

...

// as a consequence of Task.belongsTo(Project), this can be done:
task2.setProject(project);

顺便说一句,写完这个答案后,我意识到这与Vladsyslav Turak 在他的答案中解释的内容相同,但我决定将我的答案保留在这里,因为它添加了一些涉及实用方法的重要实用信息。

【讨论】:

【参考方案2】:

这是一个更普遍的问题。

主要区别在于语义。你必须决定什么是关系(一些愚蠢的例子):

人只有一只右臂。右臂属于一个人。

反过来说有点奇怪:

右臂有个男人。男人属于右臂。

你可以拥有没有右臂的人。但是单靠右臂是没用的。

如果 RightArm 和 Man 是模型,在 sequelize 中,它可能看起来像:

Man.hasOne(RightArm);      // ManId in RigthArm
RightArm.belongsTo(Man);   // ManId in RigthArm

正如您所注意到的,db 表结构也有所不同:

BelongsTo 将在源上添加 foreignKey,而 hasOne 将在目标上添加(Sequelize 在表 'RightArm' 中创建新列 'ManId' ,但不会在 ' 中创建 'RightArmId' 列人的桌子)。

我没有看到更多的差异。

【讨论】:

那么在这种情况下,我应该使用Man.hasOne(RightArm); 还是RightArm.belongsTo(Man);?还是两者都用? 在大多数情况下我会同时使用它们 我认为@KrzysztofSztompka 想说的是:根据每种情况,他可以使用或者 hasOne 或 belongsTo 考虑语义。但是没有必要设置例如:Man.hasOne(RightArm); RightArm.belongsTo(Man); 因为他们做同样的事情就是为 RighArm 设置外键。 @YangjunWang,请看下面我的回答。【参考方案3】:

我同意Krzysztof Sztompka关于两者之间的区别:

Man.hasOne(RightArm);
RightArm.belongsTo(Man);

我想回答Yangjun Wang的问题:

所以在这种情况下,我应该使用Man.hasOne(RightArm); 还是 RightArm.belongsTo(Man);?还是两者都用?

确实,Man.hasOne(RightArm); 关系和 RightArm.belongsTo(Man); 做同样的事情 - 这些关系中的每一个都会将外键 manId 添加到 RightArm 表中。

从物理数据库层的角度来看,这些方法做的事情是一样的,对于我们的数据库我们将使用哪种方法没有区别。

那么,有什么区别呢?主要区别在于 ORM 的层(在我们的例子中是 Sequalize ORM,但下面的逻辑适用于 Laravel 的 Eloquent ORM 甚至 Ruby 的 Active Record ORM)。

使用Man.hasOne(RightArm); 关系,我们将能够使用Man 模型填充该人的RightArm。如果这对我们的应用程序来说已经足够了,我们可以停下来,不要将RightArm.belongsTo(Man); 关系添加到RightArm 模型中。

但是如果我们需要获取RightArm 的所有者怎么办?如果不在 RightArm 模型上定义 RightArm.belongsTo(Man); 关系,我们将无法使用 RightArm 模型执行此操作。

另一个例子是UserPhone 模型。定义User.hasOne(Phone) 关系,我们将能够填充我们的UserPhone。如果不定义Phone.belongsTo(User) 关系,我们将无法填充Phone 的所有者(例如我们的User)。如果我们定义Phone.belongsTo(User) 关系,我们将能够获得Phone 的所有者。

因此,这里有主要区别:如果我们希望能够从两个模型中填充数据,我们需要在它们上定义关系(hasOnebelongsTo)。如果我们只得到UserPhone 就足够了,而不是PhoneUser,我们可以在User 模型上只定义User.hasOne(Phone) 关系。

上述逻辑适用于所有具有hasOnebelongsTo 关系的ORM。

我希望这能澄清你的理解。

【讨论】:

我们可以在两个型号上都使用belongsTo,否则它将不起作用?另外——如何正确定义迁移?我们是否应该在手机模型上添加列(例如user_id)?

以上是关于Sequelize ORM中HasOne和BelongsTo的区别的主要内容,如果未能解决你的问题,请参考以下文章

Sequelize 关联 hasOne,belongsTo

laravel 中的 Eloquent ORM 里,hasOne 和 belongsTo 有啥区别

NPM ORM - 保存 hasOne 字段的更新

在使用 Sequelize 作为 ORM 的 Sails 应用程序中使用原始查询

Node.js - 使用 'async' 和 'await' 和 sequelize ORM

如何在 Sequelize ORM 中插入 PostGIS GEOMETRY 点?