Sequelize ORM中HasOne与BelongsTo的区别

Jas*_*bas 33 orm node.js sequelize.js

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

文件指出:

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

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

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

Krz*_*pka 60

这是更普遍的问题.

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

男人只有一只右臂.右臂属于一个人.

说它反过来看起来有点奇怪:

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

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

在续集中如果RightArm和Men是模型,它可能看起来像:

Man.hasOne(RightArm);
RightArm.belongsTo(Man);
Run Code Online (Sandbox Code Playgroud)

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

BelongsTo将在源头上添加foreignKey,其中hasOne将在目标上添加(Sequelize在表'RightArm'中创建新列'ManId',但不在'Man'表中创建'RightArmId'列).

我没有看到任何更多的分歧.

  • 在大多数情况下,我会同时使用它们 (4认同)
  • 所以在这种情况下,我应该使用`Man.hasOne(RightArm);`还是`RightArm.belongsTo(Man);`?还是两者都使用? (3认同)
  • 我认为@KrzysztofSztompka 想说的是:根据他可以使用的每种情况,或者hasOne 或belongsTo 考虑语义。但是没有必要设置例如:`Man.hasOne(RightArm); RightArm.belongsTo(Man); ` 因为他们做同样的事情,即为 RighArm 设置外键。 (2认同)

Vla*_*rak 45

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

Man.hasOne(RightArm);
RightArm.belongsTo(Man);
Run Code Online (Sandbox Code Playgroud)

我想回答杨俊君的问题:

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

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

从物理数据库层的角度来看,这些方法做了同样的事情,对我们的数据库来说,我们将使用的确切方法没有区别.

那么,有什么区别?主要的区别奠定了ORM的层上(在我们的情况下,它是Sequalize ORM,但下面的逻辑也适用于Laravel的雄辩 ORM甚至Ruby的活动记录 ORM).

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

上面的逻辑适用于所有具有hasOnebelongsTo关系的ORM .

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

  • 很好的解释,非常感谢. (3认同)

jpa*_*vel 9

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

假设您有以下语句(来自文档):

Project.hasMany(Task);
Run Code Online (Sandbox Code Playgroud)

它创建,在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]);
Run Code Online (Sandbox Code Playgroud)

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

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

Task.belongsTo(Project);
Run Code Online (Sandbox Code Playgroud)

然后,类似地,在数据库中,tasks将创建关系中的外键,指向projects关系。但是addTasksproject实例上不会有任何方法。但是,通过这样做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);
Run Code Online (Sandbox Code Playgroud)

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

  • Project.hasMany(Task);: 在这个语句中,Project模型。Task反过来,是目标模型。
  • Task.belongsTo(Project);: 在这个语句中,Task模型。Project反过来,是目标模型。

问题是,当使用hasOnehasManybelongsTo、 和创建关联时belongsToMany,实例实用程序方法仅在模型上创建。总而言之:如果您希望在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);
Run Code Online (Sandbox Code Playgroud)

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