2016-01-18 2 views
0

Итак, я работаю над проектом на Java, но на самом деле язык здесь не имеет значения.Сохранение пользователей в хранилище данных с уникальными адресами электронной почты

Так что я хочу создавать и хранить пользователей в хранилище данных, и я пытаюсь разработать наилучший способ сделать это, чтобы я мог гарантировать, что письмо не будет использоваться более одного раза. Таким образом, обычный способ сделать это в реляционной базе данных ACID будет во время транзакции. Заблокируйте базу данных, посмотрите, существует ли электронное письмо, если оно разблокируется и завершается неудачно, иначе вставьте и разблокируйте.

Теперь это как концепция будет работать в Appengine, а также вы можете использовать транзакции. Однако, поскольку запись могла быть вставлена ​​только миллисекундами раньше, она может отсутствовать в хранилище данных из-за сильной/возможной согласованности.

Так что я думал о:

  • с использованием глобального родителя для всех пользователей, таких, что я могу затем сделать запрос по предку в моей транзакции, тем самым вынуждая его быть последние данные запрошенные. Однако при этом возникают проблемы с лимитом 1 XG-обновления в секунду. (Это подход, к которому я решил не пойти)

  • хранение электронных писем, которые вставляются в memcache в отдельный список, потому что даже если он должен быть очищен, он, вероятно, не будет очищен до запись вставляется в хранилище данных, поэтому мы могли бы выполнить поиск как в кеше, так и в хранилище данных, а если его нет, мы можем предположить, что он не будет находиться в хранилище данных. Однако, это Memcache поиск не будет частью сделки, так что все равно будет проблемы

Так что мой главный вопрос заключается в том, что ни один из этих подходов будет использовать запрос предка, так не может быть сделан как часть сделка.

Благодаря

Edit: Подумав о структуре, я рассматриваю что-то вроде этого. Мне нужно будет проверить это, когда я вернусь домой позже, и отметьте это как мой принятый ответ, если он сработает.

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 
+0

Мысль № 1: по той причине, что вы назвали этот было бы плохой идеей. Мысль № 2: Однако маловероятно, что memcache выселяется только в неправильный/правильный момент - если это так, s *** действительно попадает в вентилятор. Поэтому не делайте обычных случаев, когда необычный случай сломает вам шею. Моя мысль: считаете ли вы создание таблицы, в которой письмо является '@ Id'? Если я получаю свои факты прямо, запросы по id всегда сильно согласованы. – konqi

+0

Но они не могут быть выполнены в транзакции, если только у меня не было дочернего объекта, у которого также было письмо, а затем я могу выполнить запрос предка, чтобы проверить, присутствует ли дочерний элемент. Но это вызывает много операций db, если пользователь хочет изменить свой адрес электронной почты, так как это потребует обновления всех дочерних элементов по мере изменения идентификатора родителей, нет? – Branks

+1

Взгляните на http://stackoverflow.com/questions/24085216/appengine-python-how-to-ensure-that-users-register-with-unique-uersnames/24093300#24093300, в этом мы использовали «уникальные» имена пользователей , но подход применим для электронной почты. –

ответ

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

Я использую Python-апринт App Engine, но я подозреваю, что на Java существует аналогичная функциональность.

Вы можете сделать следующее:

  1. Используйте адрес электронной почты в качестве key_name в сущности, и
  2. Создать объект, используя get_or_insert(key_name) (docs).

Используя get_or_insert, вы гарантируете, что не будете создавать одно и то же имя более одного раза.

+0

К сожалению, ваше подозрение не так. В Java вам нужно реализовать функции 'get_or_insert' самостоятельно. – konqi

+0

Но проблема с get_or_insert заключается в том, что она действительно не сообщает, какую операцию она выполняла. Вам нужно будет проверить возвращаемый объект, чтобы узнать, было ли оно обнаружено, и вы можете выполнить другую проверку, которую хотите выполнить, перед созданием новой записи. –

+0

@TimHoffman, да, это правильно, но это не похоже на значительное ограничение. Предположительно, вы создадите по крайней мере одно свойство после создания объекта, и вы можете просто проверить, установлено ли это свойство. –

Смежные вопросы