My favorites | 中文(简体) | Sign in

键和实体组

数据库中的每个实体都有一个键,一个对于应用程序中所有实体都唯一的标识符。一个键有若干个组成部分:路径描述两个实体之间的父子关系,实体的类型,以及由应用程序分配给实体的名称,或由数据库分配的数字 ID。

类型、名称和 ID

每个实体都属于特定的类型,类型是可以由查询返回的一组实体。与表中的行不同,虽然应用程序可以在一个数据模型中建立此类限制,然而相同类型的两个实体不需要有相同的属性。数据库 API 使用 Model(或 Expando)子类的名称作为类型的名称。

例如,该类定义了名为 [Story] 的类型的 Model。

class Story(db.Model):
  title = db.StringProperty()
  author = db.StringProperty()

每个实体都有标识符。应用程序可以通过授予实例构造函数 key_name 参数(str 值)来分配自己的标识符以便在键中使用。

s = Story(key_name="xzy123")

key_name 存储为 Unicode 字符串,str 值转换为 ASCII 文本。key_name 绝不能以数字开头,绝不能采用 __*__ 形式(以两根下划线开头和结尾)。如果您的应用程序使用用户提交的数据作为实体键名(例如电子邮件地址),应用程序应首先清理值,例如用已知的字符串作为前缀以符合这些要求。

如果未指定 key_name,则第一次将实体存储到数据库中时,会向其分配数字 ID。

s2 = Story()    # s2 does not have a name or an ID.
s2.put()        # s2 is given an ID by the datastore.

一旦创建了实体,就不能更改其 ID 或名称。

提示:不能用与查询中的属性值类似的方式使用键名和 ID。但是,您可以使用命名的键,然后将该名称作为属性存储。您可以通过存储对象以分配 ID、使用 obj.key().id() 获取 ID 值、用 ID 设置属性,然后再次存储对象,以对数字 ID 执行类似的操作。

实体组、祖先和路径

每个实体都属于一个实体组,它是可以在一个事务中控制的一组实体(一个或多个)。实体组关系会让 App Engine 在分布式网络的相同部分中存储若干实体。事务会针对实体组设置数据库操作,且所有操作都会以组的形式应用。如果事务失败,则全都不应用。

当应用程序创建一个实体时,它将另一个实体分配为新实体的父实体。向新实体分配父实体会将新实体放置在与父实体相同的实体组。

没有父实体的实体是根实体。作为另一个实体的父实体的实体也可以有父实体。从某实体到根的父实体链是该实体的路径,路径的成员是该实体的祖先。实体的父实体是在创建该实体时定义的,且以后不能再更改。

每个采用指定根实体作为祖先的实体都在相同的实体组中。一个组中的所有实体都存储在相同的数据库节点中。一个事务可以修改一个组中的多个实体,或向组添加新实体(方法是以组中的现有实体作为新实体的父实体)。

有关事务的详细信息,请参阅事务

如果删除了某个作为其他实体的祖先的实体,则后续实体不会被删除。仍可以使用后续实体完整的键或路径对其进行访问。

您可以用祖先路径创建实体而无需先创建父实体。为此,您可以使用类型和键名为祖先创建一个 Key,然后将其用作新实体的父实体。所有具有同一根祖先的实体都属于同一实体组,与路径的根是否代表实际实体无关。

使用实体组的提示:

  • 仅当事务需要实体组时才使用实体组。对于实体之间的其他关系,请使用可以在查询中使用的 ReferenceProperty 属性和 Key 值。
  • 您的应用程序拥有的实体组越多(即有更多根实体),数据库就能越有效地在数据库节点之间分布实体组。更好的分布可提高创建和更新数据的性能。此外,多个用户尝试同时更新相同实体组中的实体会导致部分用户重新尝试他们的事务,因而可能导致某些用户无法提交更改。请勿将应用程序的所有实体放在一个根下。
  • 较好的实体组做法是:它们应与单个用户的数据值的大小相当或更小。
  • 实体组对查询的速度没有明显影响。

路径和键唯一性

实体的完整键(包括路径、类型、名称或数字 ID)都是唯一的并特定于该实体。在数据库中创建实体时,会分配完整的键,且不能更改其任何部分。

只要至少一个部分不同,两个不同实体的键就可以有相似的部分。例如,如果两个实体有不同的父实体,它们可以有相同的类型和名称。类似地,如果两个实体的类型不同,它们可以有相同的父实体(或没有父实体)和名称。

应用程序不应依赖于以增序(实体创建的顺序)分配的数字 ID。这是通常情况,但并无保证。