使用唯一的电子邮件地址将用户存储在数据存储中

时间:2016-01-18 10:21:04

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

所以我正在开发一个Java项目,但实际上这里的语言无关紧要。

所以我想在数据存储区中创建和存储用户,并且我正在努力找到最好的方法,这样我就可以确保不会多次使用电子邮件。因此,在关系ACID数据库上执行此操作的常规方法是在事务期间。 锁定数据库,查看电子邮件是否存在,是否存在,然后解锁并失败,否则插入和解锁。

现在这个概念可以在Appengine中使用,也可以使用事务。但是,由于该条目可能仅在几毫秒之前插入,因此可能不会出现在数据存储区中,因为它具有强大/最终的一致性。

所以我想过的事情:

  • 为所有用户使用全局父级,以便我可以在事务中执行祖先查询,从而强制它成为查询的最新数据。但是,这会导致每秒1 XG更新限制的问题。 (这是我决定不采用的方法)

  • 将插入到内存缓存中的电子邮件存储在一个单独的列表中,因为即使它被清除,它也可能在条目插入数据存储区之前无法清除,因此我们可以搜索缓存和数据存储区,如果它们都不存在,我们可以假设它不会出现在数据存储区中。但是,此memcache查找不会成为事务的一部分,因此仍然存在问题

所以我的主要问题是这些方法都不会使用祖先查询,因此不能作为事务的一部分来完成。

由于

编辑: 在考虑结构后,我正在考虑这样的事情。我将在以后回家时进行测试,并将其作为我接受的答案标记,如果有效的话。

UserBean
    @id Long id;        
    //All child elements will use UserBean as their parent


Login
    @id String id; //This will be the a hashed/base64 version of the  email address
    @Parent UserBean user;
    String emailAddress
    String hashedPassword;


start transaction

    Login login = ofy()
        .load()
        .type(Login.class)
        .key(hashEmail(emailAddress)).now();
    if (login == null) {
        fail transaction - email already in use
    }
    Insert UserBean and Login objects into datastore

2 个答案:

答案 0 :(得分:0)

我使用App Engine的Python风格,但我怀疑Java中有类似的功能。

您可以执行以下操作:

  1. 使用电子邮件地址作为实体的key_name
  2. 使用get_or_insert(key_name)docs)。
  3. 创建实体

    通过使用get_or_insert,您可以保证不会多次创建同一个实体。

答案 1 :(得分:0)

所以我有一个有效的解决方案,我很乐意在这里分享。

我的两个POJO:

@Entity
public class UserAccount {
    @Id Long _id;
    public UserAccount(){

    }
    public Long get_id() {
        return _id;
    }
}


@Entity
public class LoginBean {
    @Id String emailHash;
    //I don't make this an actual @Parent because this would affect the Id
    Ref<UserAccount> parent; 
    String email;
    String hashedPassword;
    public LoginBean(){

    }
    public LoginBean(String emailHash, Ref<UserAccount> parent, String email, String hashedPassword){
        this.parent = parent;
        this.emailHash = emailHash;
        this.email = email;
        this.hashedPassword = hashedPassword;
    }
    //All the rest of the getters and setters you want
}

然后在我的实用程序类中:

final String emailHash = getEmailHash(email);
final String passwordHash = getPasswordHash(password);

UserAccount savedUser = ofy().transact(new Work<UserAccount>() {
    public UserAccount run() {
        if (lookupByEmail(email) != null) {
            return null;
        }

        UserAccount user = new UserAccount();
        Key<UserAccount> userKey = ofy().save().entity(user).now();

        LoginBean login = new LoginBean(emailHash, Ref.create(userKey), email, passwordHash);
        ofy().save().entity(login).now();

        return user;
    }
});

进一步向下:

public LoginBean lookupByEmail(String email) {
    String emailhash = getEmailHash(email);
    LoginBean r = ofy().load().type(LoginBean .class).id(emailhash).now();
    return r;
}