关联和关系
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,Usertype defines a one-to-many relation withBooksslice type through the use ofhas_manytag, meaning aUsercan own manyBooks. When querying to the database, Pop will load all records from thebookstable that have a column nameduser_id, or the column specified withfk_idthat matches theUser.IDvalue.
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_totag was defined. This tag is mostly used to indicate that model owns its “existence” to the association field withbelongs_to. In the example above,Booktype usebelongs_toto indicate that it is owned by aUsertype. When querying to the database, Pop will load a record from theuserstable withidthat matches withBook.UserIDvalue.
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 oneFavoriteSongwithin all songs records thatUsertype like the most. When querying to the database, Pop will load a record from thesongstable that have a column nameduser_id, or the column specified withfk_idthat matches theUser.IDfield 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 betweenUsertype andAddressesslice type exists to indicate anUsercan own manyHousesand aHousecan be owned by manyUsers. It is important to notice that value formany_to_manytag 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 theaddressestable through the associative tableusers_addresses. Tableusers_addressesMUST defineaddress_idanduser_idcolumns to matchUser.IDandAddress.IDfield values. You can also define afk_idtag that will be used in the target association i.e.addressestable.
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,Songhas a column namedu_idthat references the id of theuserstable. When loadingFavoriteSong,u_idcolumn 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_manyandmany_to_manytags 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. UseEagerDefaultorEagerPreloadas 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 activateEagerPreloadmode 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 activateEagermode 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
Booksis ahas_manyassociation and it will realize that to actually update each book it will need to get theUser IDfirst. So, it proceeds to store firstUserdata so it can retrieve an ID and then use that ID to fillUserIDfield in everyBookinBooks. It updates all affected books in the database using theirIDs to target them. 它会注意到
Books是一个has_many关联,并且它会意识到要实际更新每本书,它需要首先获取User ID。因此,它会继续首先存储User数据,以便它可以检索一个 ID,然后使用该 ID 来填充UserID字段中的每个Book在Books中。它使用它们的IDs 来定位它们,从而更新数据库中所有受影响的书籍。FavoriteSongis ahas_oneassociation and it uses same logic described inhas_manyassociation. SinceUserdata was previously saved before updating all affected books, it already knows thatUserhas got anIDso it fills itsUserIDfield with that value andFavoriteSongis then updated in the database.
FavoriteSong是一个has_one关联,并且它使用在has_many关联中描述的相同逻辑。由于在更新所有受影响的书籍之前已经保存了User数据,因此它已经知道User已经获得了ID,因此它用该值填充其UserID字段,然后在数据库中更新FavoriteSong。Housesin this example is amany_to_manyrelationship and it will have to deal with two tables in this case:usersandaddresses. BecauseUserwas already stored, it already has itsID. It will then use theIDs passed with theAddressesto create the coresponding entries in the join table. 本例中的
Houses是一个many_to_many关系,并且在这种情况下它将必须处理两个表:users和addresses。因为User已经存储,所以它已经有了它的ID。然后,它将使用与Addresses一起传递的IDs 来在联接表中创建相应的条目。
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
Booksis ahas_manyassociation and it will realize that to actually store every book it will need to get theUser IDfirst. So, it proceeds to first store/create theUserdata so it can retrieve an ID and then use that ID to fill theUserIDfield in everyBookinBooks. Later it stores all books in the database. 它会注意到
Books是一个has_many关联,并且它会意识到要实际存储每本书,它需要先获取User ID。因此,它会首先存储/创建User数据,以便它可以检索一个 ID,然后使用该 ID 来填充Books中的每个Book中的UserID字段。稍后,它会将所有书籍存储在数据库中。FavoriteSongis ahas_oneassociation and it uses same logic described in thehas_manyassociation. SinceUserdata was previously saved before creating all books, it already knows thatUserhas got anIDso it fills itsUserIDfield with that value andFavoriteSongis then stored in the database.
FavoriteSong是一个has_one关联,并且它使用has_many关联中描述的相同逻辑。由于在创建所有书籍之前已经保存了User数据,因此它已经知道User有一个ID,因此它用该值填充其UserID字段,然后将FavoriteSong存储在数据库中。Housesin this example is amany_to_manyrelationship and it will have to deal with two tables, in this case:usersandaddresses. It will need to store all addresses first in theaddressestable before saving them in the many to many(join) table. BecauseUserwas 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 theAssociationCreatableStatementinterface, all other associations by default implement theAssociationCreatableinterface.
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.
在您使用已存在的关联模型提供急切创建的情况下,它不会创建它们的副本或更新它们的内容,而是简单地创建/更新与它们的关联。