在Sequelize ORM中,HasOne和BelongsTo之间的区别

时间:2016-01-02 11:27:26

标签: node.js orm sequelize.js

我正在使用sails.js ORM开发sequelize应用。关于什么时候需要使用BelongsTo和HasOne,我有点困惑。

文件说明:

  

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

     

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

除了指定这些之外还有其他区别吗?在这两种情况下,行为是否仍然相同?

3 个答案:

答案 0 :(得分:51)

这是更普遍的问题。

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

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

反过来看起来有点奇怪:

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

你可以让男人没有右臂。但是单独的右臂是无用的。

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

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

正如您所注意到的,db表结构也存在差异:

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

我没有看到任何更多的差异。

答案 1 :(得分:32)

我同意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);关系,我们将能够使用RightArm模型填充该人的Man。如果这对我们的应用程序来说足够了,我们可以停止使用它,并且不要将RightArm.belongsTo(Man);关系添加到RightArm模型。

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

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

所以,这里我们有一个主要区别:如果我们希望能够从两个模型填充数据,我们需要在它们两者上定义关系(PhonehasOne)。如果仅我们获得belongsTo的{​​{1}},而不是User的{​​{1}},我们只能定义Phone Phone模型上的关系。

上述逻辑适用于所有具有UserUser.hasOne(Phone)关系的ORM。

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

答案 2 :(得分:6)

我知道这是迟到四年的答案,但是自昨天以来,我一直在思考它,搜索文档并进行谷歌搜索。并且找不到答案使我确信正在发生的事情。今天,我得出一个结论:区别只是 不是 ,绝对是语义问题!

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

Project.hasMany(Task);

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

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关系。但是,addTasks实例上不会有任何project方法。但是,通过执行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依次是 目标 模型。
  • Task.belongsTo(Project); :在此语句中,Task 模型。 Project依次是 目标 模型。

问题是,当使用hasOnehasManybelongsTobelongsToMany创建关联时,实例实用程序方法仅在 < strong>源 模型。这就解释了为什么要在ProjectTask实例中都创建实用程序方法的原因,即使在数据库本身中,也必须使用两个语句来描述相同的关联,具有相同的冗余效果(在指向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在他的答案中解释的是一样的,但是我决定保留我的答案,因为它添加了一些涉及实用方法的重要实用信息