没有祖先的Google App Engine HRD查询

时间:2012-04-05 13:14:35

标签: java google-app-engine google-cloud-datastore objectify

我有一个用Java编写的GAE项目,我对HRD和一个我不确定如何解决的问题有一些想法。

基本上我的系统中有用户。用户由用户标识,用户名,电子邮件和密码组成。每次创建新用户时,我都想检查是否已经有用户具有相同的用户ID(应该永远不会发生),用户名或电子邮件。

用户标识是关键所在,所以我认为用它来获取是一致的。但是,当我进行查询(并使用过滤器)查找具有相同用户名或电子邮件的可能用户时,我无法确定结果是否一致。因此,如果有人在几秒钟之前创建了具有相同用户名或电子邮件的用户,我可能无法通过查询找到它。我知道祖先习惯于解决这个问题,但是如果我没有用于查询的祖先呢?用户没有父母。

我很高兴听到您对此的看法,以及在这些情况下被认为是最佳做法。如果改变了什么,我正在使用Objectify for GAE。

4 个答案:

答案 0 :(得分:7)

我不建议您为用户实体使用电子邮件或任何其他自然密钥。用户更改了他们的电子邮件地址,并且当用户更改其电子邮件时,您不希望最终重写数据库中的所有外键引用。

以下是关于如何解决此问题的简短说明:

https://groups.google.com/d/msg/google-appengine/NdUAY0crVjg/3fJX3Gn3cOYJ

创建一个单独的EmailLookup实体,其@Id是电子邮件地址的规范化形式(我只是小写一切 - 技术上不正确但在用户意外地将Joe@example.com资本化时会节省很多痛苦)。我的EmailLookup看起来像这样:

@Entity(name="Email")
public class EmailLookup {
    /** Use this method to normalize email addresses for lookup */
    public static String normalize(String email) {
        return email.toLowerCase();
    }

    @Id String email;
    @Index long personId;

    public EmailLookup(String email, long personId) {
        this.email = normalize(email);
        this.personId = personId;
    }
}

我的用户实体中还有一个(未规范化的)电子邮件字段,我在发送出站电子邮件时使用该字段(保留案例以防万一对某人有用)。当有人使用特定电子邮件创建帐户时,我会按照XG事务中的键加载/创建EmailLookup和用户实体。这可以保证任何个人电子邮件地址都是唯一的。

同样的策略适用于任何其他类型的独特价值; facebook id,username等。

答案 1 :(得分:3)

围绕HRD eventual consistency的方法是使用get代替query。要做到这一点,您需要生成自然ID,例如生成包含您在请求中收到的数据的ID:电子邮件和用户名。

由于HRD中的get具有很强的一致性,您将能够可靠地检查用户是否已经存在。

例如,可读的自然ID将是:

String naturalUserId = userEmail + "-" + userName;

注意:在实践中,电子邮件是唯一的。所以这是一个很好的自然ID。无需为其添加虚构的用户名。

答案 2 :(得分:-1)

您还可以启用跨群组交易(请参阅https://developers.google.com/appengine/docs/java/datastore/overview#Cross_Group_Transactions),然后在一个事务中查找用户并创建一个新用户,如果有帮助的话。

答案 3 :(得分:-1)

建议避免索引字段和查询,除非您有其他用途。以下是我之前使用key_name(Python)所做的事情(因为实体id需要是int)。易于使用key_name或id用于需要链接到用户的其他实体:

username = self.request.get('username')
usernameLower = username.lower()
rec = user.get_by_key_name(usernameLower)
if rec is None:
    U = user(
        key_name = usernameLower,
        username = username,
        etc...)
    U.put()
else:
    self.response.out.write(yourMessageHere)