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

Storing users in datastore with unique email addresses

所以我在 Java 从事一个项目,但实际上语言在这里并不重要。

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

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

所以我想到的事情:

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

谢谢

编辑: 在考虑了结构之后,我正在考虑这样的事情。待会儿回家再测试一下,如果有效,我会把它标记为我接受的答案。

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

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

您可以执行以下操作:

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

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

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

我的两个 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
}

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

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;
}