我正在使用sails.js ORM开发sequelize应用。关于什么时候需要使用BelongsTo和HasOne,我有点困惑。
文件说明:
BelongsTo 关联是其中外键的关联 源模型中存在一对一的关系。
HasOne 关联是其中外键的关联 目标模型上存在一对一的关系。
除了指定这些之外还有其他区别吗?在这两种情况下,行为是否仍然相同?
答案 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
模型执行此操作。
另外一个例子是User
和Phone
模型。定义User.hasOne(Phone)
关系,我们将能够填充User
的{{1}}。如果不定义Phone
关系,我们将无法填充Phone.belongsTo(User)
的所有者(例如我们的Phone
)。如果我们定义User
关系,我们就可以获得Phone.belongsTo(User)
的所有者。
所以,这里我们有一个主要区别:如果我们希望能够从两个模型填充数据,我们需要在它们两者上定义关系(Phone
和hasOne
)。如果仅我们获得belongsTo
的{{1}},而不是User
的{{1}},我们只能定义Phone
Phone
模型上的关系。
上述逻辑适用于所有具有User
和User.hasOne(Phone)
关系的ORM。
我希望这能澄清你的理解。
答案 2 :(得分:6)
我知道这是迟到四年的答案,但是自昨天以来,我一直在思考它,搜索文档并进行谷歌搜索。并且找不到答案使我确信正在发生的事情。今天,我得出一个结论:区别只是 不是 ,绝对是语义问题!
假设您有以下语句(from the docs):
Project.hasMany(Task);
它在Project
模型中为Project
的实例创建一些实用程序方法,例如:addTask
,setTask
等。因此,您可以执行以下操作:< / 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
依次是 目标 模型。问题是,当使用hasOne
,hasMany
,belongsTo
和belongsToMany
创建关联时,实例实用程序方法仅在 < strong>源 模型。这就解释了为什么要在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在他的答案中解释的是一样的,但是我决定保留我的答案,因为它添加了一些涉及实用方法的重要实用信息