关联和关系
9 分钟阅读
Associations and Relationships 关联和关系
Associations are the Pop way to define a relation between two objects in the database. In this chapter, you’ll learn how to define associations using struct tags; and how to manipulate them with the Eager()
modifier.
关联是定义数据库中两个对象之间关系的流行方式。在本章中,您将学习如何使用结构体标签定义关联;以及如何使用 Eager()
修饰符操作它们。
Example 示例
|
|
Available Struct Tags 可用的结构体标签
Using the above example code below is a list of available struct tags and how to use them.
使用上面的示例代码,下面列出了可用的结构体标签以及如何使用它们。
has_many
: This tag is used to describe one-to-many relationships in the database. In the example,User
type defines a one-to-many relation withBooks
slice type through the use ofhas_many
tag, meaning aUser
can own manyBooks
. When querying to the database, Pop will load all records from thebooks
table that have a column nameduser_id
, or the column specified withfk_id
that matches theUser.ID
value.
has_many
:此标签用于描述数据库中的一对多关系。在示例中,User
类型通过使用has_many
标签定义了与Books
切片类型的一对多关系,这意味着User
可以拥有许多Books
。在查询数据库时,Pop 将加载books
表中所有具有名为user_id
的列的记录,或使用fk_id
指定的列,该列与User.ID
值匹配。belongs_to
: This tag is used to describe the owner in the relationship. An owner represents a highly coupled dependency between the model and the target association field wherebelongs_to
tag was defined. This tag is mostly used to indicate that model owns its “existence” to the association field withbelongs_to
. In the example above,Book
type usebelongs_to
to indicate that it is owned by aUser
type. When querying to the database, Pop will load a record from theusers
table withid
that matches withBook.UserID
value.
belongs_to
:此标记用于描述关系中的所有者。所有者表示模型与目标关联字段之间的高度耦合依赖关系,其中定义了belongs_to
标记。此标记主要用于指示模型拥有其与belongs_to
关联字段的“存在”。在上面的示例中,Book
类型使用belongs_to
指示它归User
类型所有。在查询数据库时,Pop 将从users
表中加载记录,其中id
与Book.UserID
值匹配。has_one
: This tag is used to describe one-to-one relationships in the database. In the example above, there is only oneFavoriteSong
within all songs records thatUser
type like the most. When querying to the database, Pop will load a record from thesongs
table that have a column nameduser_id
, or the column specified withfk_id
that matches theUser.ID
field value.
has_one
:此标记用于描述数据库中的一对一关系。在上面的示例中,所有歌曲记录中只有一个FavoriteSong
最喜欢的User
类型。在查询数据库时,Pop 将从songs
表中加载一条记录,该记录具有名为user_id
的列,或使用fk_id
指定的列,该列与User.ID
字段值匹配。many_to_many
: This tag is used to describe many-to-many relationships in the database. In the example above, the relationship betweenUser
type andAddresses
slice type exists to indicate anUser
can own manyHouses
and aHouse
can be owned by manyUsers
. It is important to notice that value formany_to_many
tag is the associative table that connects both sides in the relationship; in the example above this value is defined asusers_addresses
. When querying to the database, Pop will load all records from theaddresses
table through the associative tableusers_addresses
. Tableusers_addresses
MUST defineaddress_id
anduser_id
columns to matchUser.ID
andAddress.ID
field values. You can also define afk_id
tag that will be used in the target association i.e.addresses
table.
many_to_many
:此标记用于描述数据库中的多对多关系。在上面的示例中,User
类型和Addresses
切片类型之间的关系存在,以指示一个User
可以拥有许多Houses
,而一个House
可以被许多Users
拥有。请注意,many_to_many
标记的值是连接关系中双方的关联表;在上面的示例中,此值定义为users_addresses
。在查询数据库时,Pop 将通过关联表users_addresses
从addresses
表加载所有记录。表users_addresses
必须定义address_id
和user_id
列以匹配User.ID
和Address.ID
字段值。您还可以定义一个fk_id
标记,该标记将用于目标关联中,即addresses
表。fk_id
: This tag can be used to define the column name in the target association that matches model ID. In the example above,Song
has a column namedu_id
that references the id of theusers
table. When loadingFavoriteSong
,u_id
column will be used instead ofuser_id
.
fk_id
:此标记可用于定义目标关联中与模型 ID 匹配的列名。在上面的示例中,Song
有一个名为u_id
的列,该列引用users
表的 ID。在加载FavoriteSong
时,将使用u_id
列而不是user_id
。order_by
: This tag can be used in combination withhas_many
andmany_to_many
tags to indicate the order for the association when loading. The format to use isorder_by:"<column_name> <asc | desc>"
order_by
:此标签可与has_many
和many_to_many
标签结合使用,以指示加载时的关联顺序。要使用的格式为order_by:"<column_name> <asc | desc>"
Loading Associations 加载关联
Pop currently provides two modes for loading associations; each mode will affect the way pop loads associations and queries to the database.
Pop 目前提供两种加载关联的模式;每种模式都会影响 pop 加载关联和查询数据库的方式。
Eager. Default mode. By enabling this mode, pop will perform “n” queries for every association defined in the model. This means more hits to the database in order to not affect memory use.
Eager。默认模式。通过启用此模式,pop 将对模型中定义的每个关联执行“n”个查询。这意味着更多地访问数据库,以便不影响内存使用。
EagerPreload. Optional mode. By enabling this mode, pop will perform one query for every association defined in the model. This mode will hit the database with a reduced frequency by sacrifing more memory space.
EagerPreload。可选模式。通过启用此模式,pop 将对模型中定义的每个关联执行一个查询。此模式将以牺牲更多内存空间为代价,减少访问数据库的频率。
pop.SetEagerMode
: Pop allows enabling any of these modes globally which will affect ALL queries handle performance. UseEagerDefault
orEagerPreload
as parameter to activate any of these modes.
pop.SetEagerMode
:Pop 允许全局启用这些模式中的任何一种,这将影响所有查询处理性能。使用EagerDefault
或EagerPreload
作为参数来激活这些模式中的任何一种。tx.EagerPreload | q.EagerPreload
: Pop allows developers to take control in which situations they want Pop to perform any of these modes when necessary. This method will activateEagerPreload
mode only for the query in action.
tx.EagerPreload | q.EagerPreload
:Pop 允许开发人员控制在必要时希望 Pop 执行这些模式中的哪一种的情况。此方法仅对正在执行的查询激活EagerPreload
模式。tx.Eager | q.Eager
: Pop allows developers to take control in which situations they want Pop to perform any of these modes when necessary. This method will activateEager
mode only for the query in action.
tx.Eager | q.Eager
:Pop 允许开发人员控制在必要时希望 Pop 在哪些情况下执行这些模式之一。此方法仅对操作中的查询激活Eager
模式。
Eager Mode 急切模式
The pop.Connection.Eager()
method tells Pop to load the associations for a model once that model is loaded from the database. This mode will perform “n” queries for every association defined in the model.
pop.Connection.Eager()
方法告诉 Pop 在从数据库加载模型后立即加载该模型的关联。此模式将对模型中定义的每个关联执行“n”个查询。
|
|
Eager
mode will:
Eager
模式将:
- Load all users. 加载所有用户。
|
|
- Iterate on every user and load its associations: 迭代每个用户并加载其关联:
SELECT * FROM books WHERE user_id=1)
SELECT * FROM books WHERE user_id=2)
SELECT * FROM books WHERE user_id=3)
EagerPreload Mode EagerPreload 模式
The pop.Connection.EagerPreload()
method tells Pop to load the associations for a model once that model is loaded from the database. This mode will hit the database with a reduced frequency by sacrifing more memory space.
pop.Connection.EagerPreload()
方法告诉 Pop 在从数据库加载模型后立即加载该模型的关联。此模式将通过牺牲更多内存空间来减少与数据库的交互频率。
|
|
EagerPreload
mode will:
EagerPreload
模式将:
- Load all users. 加载所有用户。
SELECT * FROM users;
- Load associations for all users in one single query. 在单个查询中加载所有用户的关联。
SELECT * FROM books WHERE user_id IN (1,2,3))
Load Specific Associations 加载特定关联
By default Eager
and EagerPreload
will load all the assigned associations for the model. To specify which associations should be loaded you can pass in the names of those fields to the Eager
or EagerPreload
methods and only those associations will be loaded.
默认情况下, Eager
和 EagerPreload
将加载模型的所有已分配关联。要指定应加载哪些关联,可以将这些字段的名称传递给 Eager
或 EagerPreload
方法,并且只会加载这些关联。
|
|
Pop also allows you to eager load nested associations by using the .
character to concatenate them. Take a look at the example below.
Pop 还允许您使用 .
字符连接嵌套关联以急切加载它们。请看下面的例子。
|
|
Loading Associations for an Existing Model 加载现有模型的关联
The pop.Connection.Load()
method takes a model struct, that has already been populated from the database, and an optional list of associations to load.
pop.Connection.Load()
方法采用一个模型结构,该结构已从数据库中填充,以及一个要加载的关联的可选列表。
|
|
The Load
method will not retrieve the User
from the database, only its associations.
Load
方法不会从数据库中检索 User
,只会检索其关联。
Flat Nested Creation 扁平嵌套创建
Pop allows you to create the models and their associations with other models in one step by default. You no longer need to create every association separately anymore. Pop will even create join table records for many_to_many
associations.
默认情况下,Pop 允许您一步创建模型及其与其他模型的关联。您不再需要分别创建每个关联。Pop 甚至会为 many_to_many
关联创建联接表记录。
Assuming the following pieces of pseudo-code:
假设以下伪代码片段:
|
|
It will notice
Books
is ahas_many
association and it will realize that to actually update each book it will need to get theUser ID
first. So, it proceeds to store firstUser
data so it can retrieve an ID and then use that ID to fillUserID
field in everyBook
inBooks
. It updates all affected books in the database using theirID
s to target them. 它会注意到
Books
是一个has_many
关联,并且它会意识到要实际更新每本书,它需要首先获取User ID
。因此,它会继续首先存储User
数据,以便它可以检索一个 ID,然后使用该 ID 来填充UserID
字段中的每个Book
在Books
中。它使用它们的ID
s 来定位它们,从而更新数据库中所有受影响的书籍。FavoriteSong
is ahas_one
association and it uses same logic described inhas_many
association. SinceUser
data was previously saved before updating all affected books, it already knows thatUser
has got anID
so it fills itsUserID
field with that value andFavoriteSong
is then updated in the database.
FavoriteSong
是一个has_one
关联,并且它使用在has_many
关联中描述的相同逻辑。由于在更新所有受影响的书籍之前已经保存了User
数据,因此它已经知道User
已经获得了ID
,因此它用该值填充其UserID
字段,然后在数据库中更新FavoriteSong
。Houses
in this example is amany_to_many
relationship and it will have to deal with two tables in this case:users
andaddresses
. BecauseUser
was already stored, it already has itsID
. It will then use theID
s passed with theAddresses
to create the coresponding entries in the join table. 本例中的
Houses
是一个many_to_many
关系,并且在这种情况下它将必须处理两个表:users
和addresses
。因为User
已经存储,所以它已经有了它的ID
。然后,它将使用与Addresses
一起传递的ID
s 来在联接表中创建相应的条目。
For a belongs_to
association like shown in the example below, it fills its UserID
field before being saved in the database.
对于如下例所示的 belongs_to
关联,它会在保存到数据库之前填充其 UserID
字段。
|
|
Eager Creation 急切创建
Pop also allows you to create models and embed the creation of their associations in one step as well.
Pop 还允许您创建模型并在一个步骤中嵌入其关联的创建。
Assuming the following pieces of pseudo-code:
假设以下伪代码片段:
|
|
It will notice
Books
is ahas_many
association and it will realize that to actually store every book it will need to get theUser ID
first. So, it proceeds to first store/create theUser
data so it can retrieve an ID and then use that ID to fill theUserID
field in everyBook
inBooks
. Later it stores all books in the database. 它会注意到
Books
是一个has_many
关联,并且它会意识到要实际存储每本书,它需要先获取User ID
。因此,它会首先存储/创建User
数据,以便它可以检索一个 ID,然后使用该 ID 来填充Books
中的每个Book
中的UserID
字段。稍后,它会将所有书籍存储在数据库中。FavoriteSong
is ahas_one
association and it uses same logic described in thehas_many
association. SinceUser
data was previously saved before creating all books, it already knows thatUser
has got anID
so it fills itsUserID
field with that value andFavoriteSong
is then stored in the database.
FavoriteSong
是一个has_one
关联,并且它使用has_many
关联中描述的相同逻辑。由于在创建所有书籍之前已经保存了User
数据,因此它已经知道User
有一个ID
,因此它用该值填充其UserID
字段,然后将FavoriteSong
存储在数据库中。Houses
in this example is amany_to_many
relationship and it will have to deal with two tables, in this case:users
andaddresses
. It will need to store all addresses first in theaddresses
table before saving them in the many to many(join) table. BecauseUser
was already stored, it already has anID
. * This is a special case to deal with, since this behavior is different from all other associations, it is solved by implementing theAssociationCreatableStatement
interface, all other associations by default implement theAssociationCreatable
interface.
Houses
在此示例中是一个many_to_many
关系,它将处理两个表,在本例中为:users
和addresses
。它需要先将所有地址存储在addresses
表中,然后再将它们保存在多对多(联接)表中。因为User
已存储,所以它已具有ID
。* 这是一个需要处理的特殊情况,因为此行为与所有其他关联不同,通过实现AssociationCreatableStatement
接口来解决,默认情况下所有其他关联都实现AssociationCreatable
接口。
For a belongs_to
association like shown in the example below, it will need to first create the User
to retrieve its ID value and then fill its UserID
field before being saved in the database.
对于如下例所示的 belongs_to
关联,它需要首先创建 User
以检索其 ID 值,然后在将其保存在数据库中之前填充其 UserID
字段。
|
|
In the case where you feed the eager create with associated models that already exist, it will, instead of creating duplicates of them or updating the contents of them, simply create/update the associations with them.
在您使用已存在的关联模型提供急切创建的情况下,它不会创建它们的副本或更新它们的内容,而是简单地创建/更新与它们的关联。