Sequelize ORM中HasOne和BelongsTo的区别
Difference between HasOne and BelongsTo in Sequelize ORM
我正在开发 sails.js app with sequelize ORM。我有点困惑什么时候需要使用 BelongsTo 和 HasOne。
文档指出:
BelongsTo associations are associations where the foreign key for the
one-to-one relation exists on the source model.
HasOne associations are associations where the foreign key for the
one-to-one relation exists on the target model.
除了指定的地方,还有什么不同吗?在这两种情况下,行为是否仍然相同?
这是一个比较普遍的问题。
主要区别在于语义。你必须决定关系是什么(一些愚蠢的例子):
人只有一只右臂。右臂属于一个人。
反过来说有点奇怪:
右臂有个男人。男人属于右臂。
你可以拥有没有右臂的男人。但是单靠右臂是没用的
如果 RightArm 和 Man 是模型,在 sequelize 中,它可能看起来像:
Man.hasOne(RightArm); // ManId in RigthArm
RightArm.belongsTo(Man); // ManId in RigthArm
正如您所注意到的,db table 结构也存在差异:
BelongsTo 将在源上添加外键,而 hasOne 将在目标上添加外键(Sequelize 在 table [=38= 中创建新列 'ManId' ],但不会在 'Man' table).
中创建 'RightArmId' 列
我没有看到更多差异。
我同意 Krzysztof Sztompka 关于两者之间的区别:
Man.hasOne(RightArm);
RightArm.belongsTo(Man);
我想回答Yangjun Wang的问题:
So in this case, should I use either Man.hasOne(RightArm);
or
RightArm.belongsTo(Man);
? Or use them both?
确实 Man.hasOne(RightArm);
关系和 RightArm.belongsTo(Man);
做同样的事情 - 这些关系中的每一个都会将外键 manId
添加到 RightArm
table.
从物理数据库层的角度来看,这些方法做同样的事情,对于我们的数据库,我们将使用哪种确切方法没有区别。
那么,有什么区别呢?主要区别在于 ORM 层(在我们的例子中是 Sequalize ORM, but the logic below applies to Laravel's Eloquent ORM or even to Ruby's Active Record ORM)。
使用 Man.hasOne(RightArm);
关系,我们将能够使用 Man
模型填充男人的 RightArm
。如果这对我们的应用程序来说足够了,我们可以停止它并且不将 RightArm.belongsTo(Man);
关系添加到 RightArm
模型。
但是如果我们需要得到 RightArm
的所有者怎么办?如果不在 RightArm
模型上定义 RightArm.belongsTo(Man);
关系,我们将无法使用 RightArm
模型执行此操作。
另一个例子是 User
和 Phone
模型。定义 User.hasOne(Phone)
关系,我们将能够填充 User
的 Phone
。如果不定义 Phone.belongsTo(User)
关系,我们将无法填充 Phone
的所有者(例如我们的 User
)。如果我们定义 Phone.belongsTo(User)
关系,我们将能够获得 Phone
的所有者。
所以,这里我们有主要区别:如果我们希望能够从两个模型中填充数据,我们需要在两个模型上定义关系(hasOne
和 belongsTo
) .如果我们只得到就够了,比如User
的Phone
,但是Phone
的User
不行,我们可以只定义User.hasOne(Phone)
User
模型上的关系。
上述逻辑适用于所有具有 hasOne
和 belongsTo
关系的 ORM。
我希望这能澄清你的理解。
我知道这是一个迟了 4 年的答案,但从昨天开始我一直在思考它、搜索文档和谷歌搜索。并且无法找到让我相信正在发生的事情的答案。今天我得出一个结论:区别是不是只是语义问题,绝对!
假设您有以下语句 (from the docs):
Project.hasMany(Task);
它在 Project
模型中,在 Project
的实例上创建了一些实用方法,例如:addTask
、setTask
等。所以你可以做类似的事情:
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
是source 模型。 Task
又是 target 模型。
Task.belongsTo(Project);
:在这个语句中,Task
是source 模型。 Project
又是 target 模型。
事实是,当使用 hasOne
、hasMany
、belongsTo
和 belongsToMany
创建关联时,实例实用程序方法仅在 来源模型。总结:如果你想在 Project
和 Task
实例中创建实用方法,你必须使用这两个语句来描述相同的关联。在数据库本身中,两者将具有相同的冗余效果(在指向 projects
关系的主键的 tasks
关系上创建外键):
// 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 在他的答案中解释的是同一件事,但我决定将我的答案保留在这里,因为它添加了一些涉及实用程序的重要实用信息方法的东西。
One-to-One belongTo 或 hasOne
使用右臂示例,以及Sequelize自己的文档。我们必须要问的问题是,人没有右臂还能活吗?或者没有男人右臂可以生存吗?确定我们希望外键存在的位置就是回答这个问题。举个更实际的例子吧
假设您有一个社区网站。您的用户全部由单个配置文件 model(或用户 model)表示。但在社区中,您还会有管理员和 mod 操作员,他们都有自己的权限集,甚至可能是不同类型的配置文件。不要向用户 model 添加 admin/mod 特定字段,最好创建一个单独的 model 来表示 admin/mod.
这是基本用户 model 的样子(忽略约束和验证):
class User extends Model {
static associate(models) {}
}
User.init(
{
username: DataTypes.STRING(25),
password: DataTypes.STRING(50)
}
)
现在这里有一个 model 表示管理员或 mod,它旨在扩展用户 model:
class Staff extends Model {
static associate(models) {}
{
Staff.init(
{
permissions: DataTypes.ARRAY(DataTypes.STRING),
roleType: DataTypes.STRING(20),
}
)
所以我们问自己,没有 admin/mod 用户还能存在吗? admin/mod 可以没有用户存在吗?用户不必是员工才能使用您的服务,但 admin/mod 仍然需要用户名和密码才能登录。您可以将这些字段添加到 Staff model,但事实是,它会重复信息并使事情更难跟踪。
本质上,admin/mod 将具有与普通用户相同的属性,只是具有特殊能力。如果您另有打算,我仍然会维护一个 BaseUser model 来组织和保持每个 model 的共同点。 admin/mod 帐户仍然有用户名和密码,可能还有电子邮件。否则,您最终会拥有两个拥有相同信息的用户,并且在一个可能令人困惑且难以管理的社区中。
确定用户不需要与其关联的Staff对象存在,因此我们不应该将外键放在用户配置文件中。不过,这仍然不能完全回答我们的问题。请记住,hasOne()
将 FK 放在目标 model 上,而 belongsTo()
将 FK 放在源上。所以我们可以说满足 FK 要求的 Staff.belongsTo(User)
或 User.hasOne(Staff)
必须存在于工作人员 model.
无论是在 Staff model 上放置 belongsTo() 还是在 User model 上放置 hasOne() 都是语义问题,并不重要。两者都会将员工 model 与用户 model 相关联,从而允许您执行 User.getStaff() 方法。如果您希望能够从 Staff 实例获取用户帐户,您可以添加一个引用列,而无需在我们的 Staff model 上创建实际关联(这不会添加约束或关联,只是因为它暗示,参考):
user: {
type: DataTypes.INTEGER,
references: {
model: User,
key: 'userId'
}
}
希望对您有所帮助。
我正在开发 sails.js app with sequelize ORM。我有点困惑什么时候需要使用 BelongsTo 和 HasOne。
文档指出:
BelongsTo associations are associations where the foreign key for the one-to-one relation exists on the source model.
HasOne associations are associations where the foreign key for the one-to-one relation exists on the target model.
除了指定的地方,还有什么不同吗?在这两种情况下,行为是否仍然相同?
这是一个比较普遍的问题。
主要区别在于语义。你必须决定关系是什么(一些愚蠢的例子):
人只有一只右臂。右臂属于一个人。
反过来说有点奇怪:
右臂有个男人。男人属于右臂。
你可以拥有没有右臂的男人。但是单靠右臂是没用的
如果 RightArm 和 Man 是模型,在 sequelize 中,它可能看起来像:
Man.hasOne(RightArm); // ManId in RigthArm
RightArm.belongsTo(Man); // ManId in RigthArm
正如您所注意到的,db table 结构也存在差异:
BelongsTo 将在源上添加外键,而 hasOne 将在目标上添加外键(Sequelize 在 table [=38= 中创建新列 'ManId' ],但不会在 'Man' table).
中创建 'RightArmId' 列我没有看到更多差异。
我同意 Krzysztof Sztompka 关于两者之间的区别:
Man.hasOne(RightArm);
RightArm.belongsTo(Man);
我想回答Yangjun Wang的问题:
So in this case, should I use either
Man.hasOne(RightArm);
orRightArm.belongsTo(Man);
? Or use them both?
确实 Man.hasOne(RightArm);
关系和 RightArm.belongsTo(Man);
做同样的事情 - 这些关系中的每一个都会将外键 manId
添加到 RightArm
table.
从物理数据库层的角度来看,这些方法做同样的事情,对于我们的数据库,我们将使用哪种确切方法没有区别。
那么,有什么区别呢?主要区别在于 ORM 层(在我们的例子中是 Sequalize ORM, but the logic below applies to Laravel's Eloquent ORM or even to Ruby's Active Record ORM)。
使用 Man.hasOne(RightArm);
关系,我们将能够使用 Man
模型填充男人的 RightArm
。如果这对我们的应用程序来说足够了,我们可以停止它并且不将 RightArm.belongsTo(Man);
关系添加到 RightArm
模型。
但是如果我们需要得到 RightArm
的所有者怎么办?如果不在 RightArm
模型上定义 RightArm.belongsTo(Man);
关系,我们将无法使用 RightArm
模型执行此操作。
另一个例子是 User
和 Phone
模型。定义 User.hasOne(Phone)
关系,我们将能够填充 User
的 Phone
。如果不定义 Phone.belongsTo(User)
关系,我们将无法填充 Phone
的所有者(例如我们的 User
)。如果我们定义 Phone.belongsTo(User)
关系,我们将能够获得 Phone
的所有者。
所以,这里我们有主要区别:如果我们希望能够从两个模型中填充数据,我们需要在两个模型上定义关系(hasOne
和 belongsTo
) .如果我们只得到就够了,比如User
的Phone
,但是Phone
的User
不行,我们可以只定义User.hasOne(Phone)
User
模型上的关系。
上述逻辑适用于所有具有 hasOne
和 belongsTo
关系的 ORM。
我希望这能澄清你的理解。
我知道这是一个迟了 4 年的答案,但从昨天开始我一直在思考它、搜索文档和谷歌搜索。并且无法找到让我相信正在发生的事情的答案。今天我得出一个结论:区别是不是只是语义问题,绝对!
假设您有以下语句 (from the docs):
Project.hasMany(Task);
它在 Project
模型中,在 Project
的实例上创建了一些实用方法,例如:addTask
、setTask
等。所以你可以做类似的事情:
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
是source 模型。Task
又是 target 模型。Task.belongsTo(Project);
:在这个语句中,Task
是source 模型。Project
又是 target 模型。
事实是,当使用 hasOne
、hasMany
、belongsTo
和 belongsToMany
创建关联时,实例实用程序方法仅在 来源模型。总结:如果你想在 Project
和 Task
实例中创建实用方法,你必须使用这两个语句来描述相同的关联。在数据库本身中,两者将具有相同的冗余效果(在指向 projects
关系的主键的 tasks
关系上创建外键):
// 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 在他的答案中解释的是同一件事,但我决定将我的答案保留在这里,因为它添加了一些涉及实用程序的重要实用信息方法的东西。
One-to-One belongTo 或 hasOne
使用右臂示例,以及Sequelize自己的文档。我们必须要问的问题是,人没有右臂还能活吗?或者没有男人右臂可以生存吗?确定我们希望外键存在的位置就是回答这个问题。举个更实际的例子吧
假设您有一个社区网站。您的用户全部由单个配置文件 model(或用户 model)表示。但在社区中,您还会有管理员和 mod 操作员,他们都有自己的权限集,甚至可能是不同类型的配置文件。不要向用户 model 添加 admin/mod 特定字段,最好创建一个单独的 model 来表示 admin/mod.
这是基本用户 model 的样子(忽略约束和验证):
class User extends Model {
static associate(models) {}
}
User.init(
{
username: DataTypes.STRING(25),
password: DataTypes.STRING(50)
}
)
现在这里有一个 model 表示管理员或 mod,它旨在扩展用户 model:
class Staff extends Model {
static associate(models) {}
{
Staff.init(
{
permissions: DataTypes.ARRAY(DataTypes.STRING),
roleType: DataTypes.STRING(20),
}
)
所以我们问自己,没有 admin/mod 用户还能存在吗? admin/mod 可以没有用户存在吗?用户不必是员工才能使用您的服务,但 admin/mod 仍然需要用户名和密码才能登录。您可以将这些字段添加到 Staff model,但事实是,它会重复信息并使事情更难跟踪。
本质上,admin/mod 将具有与普通用户相同的属性,只是具有特殊能力。如果您另有打算,我仍然会维护一个 BaseUser model 来组织和保持每个 model 的共同点。 admin/mod 帐户仍然有用户名和密码,可能还有电子邮件。否则,您最终会拥有两个拥有相同信息的用户,并且在一个可能令人困惑且难以管理的社区中。
确定用户不需要与其关联的Staff对象存在,因此我们不应该将外键放在用户配置文件中。不过,这仍然不能完全回答我们的问题。请记住,hasOne()
将 FK 放在目标 model 上,而 belongsTo()
将 FK 放在源上。所以我们可以说满足 FK 要求的 Staff.belongsTo(User)
或 User.hasOne(Staff)
必须存在于工作人员 model.
无论是在 Staff model 上放置 belongsTo() 还是在 User model 上放置 hasOne() 都是语义问题,并不重要。两者都会将员工 model 与用户 model 相关联,从而允许您执行 User.getStaff() 方法。如果您希望能够从 Staff 实例获取用户帐户,您可以添加一个引用列,而无需在我们的 Staff model 上创建实际关联(这不会添加约束或关联,只是因为它暗示,参考):
user: {
type: DataTypes.INTEGER,
references: {
model: User,
key: 'userId'
}
}
希望对您有所帮助。