5

У меня есть проект GAE, написанный на Java, и у меня есть некоторые мысли о HRD и проблема, которую я не знаю, как решить.Google App Engine HRD запрос без предка

В основном у меня есть пользователи в моей системе. Пользователь состоит из идентификатора пользователя, имени пользователя, электронной почты и пароля. Каждый раз, когда я создаю нового пользователя, я хочу проверить, что еще нет пользователя с тем же идентификатором пользователя (никогда не должно быть), именем пользователя или электронной почтой.

Идентификатор пользователя является ключом, поэтому я думаю, что выполнение этого будет непротиворечивым. Однако, когда я делаю запрос (и использую фильтр), чтобы найти возможных пользователей с одинаковым именем пользователя или электронной почтой, я не могу быть уверен, что результаты будут согласованы. Поэтому, если кто-то создал пользователя с тем же именем пользователя или электронной почтой пару секунд назад, я, возможно, не найду его с моим запросом. Я понимаю, что предки используются для решения этой проблемы, но что, если у меня нет предка для использования в запросе? У пользователя нет родителя.

Я был бы рад услышать ваши мысли об этом, и то, что считается лучшей практикой в ​​таких ситуациях. Я использую Objectify для GAE, если это что-то меняет.

+1

Я задал вопрос, который может оказаться очень полезным. (http://stackoverflow.com/questions/6584435/how-can-i-create-two-unique-queriable-fields-for-a-gae-datastore-data-model) Мне тоже нужно было хранить уникальную информацию для моих пользователей. В моем случае мне нужно было сохранить как уникальный адрес электронной почты, так и уникальный идентификатор пользователя для каждого пользователя. Это немного сложно с HRD, но я пришел к надежному решению. ... cont ... – RLH

+0

Единственная проблема с моей ситуацией в том, что моя реализация создания учетной записи (см. мой ответ) не будет хорошо масштабироваться. Это было хорошо в моих обстоятельствах, потому что мое приложение GAE очень мало и имеет медленную рутину новых пользователей (1 или 2 месяца). Кроме того, эта информация находится на Python, но код прост - вы должны иметь возможность повторно -транслировать его на Java с относительной легкостью. – RLH

+0

@RLH Спасибо за ваш вклад, всегда интересно видеть разные решения, но я не думаю, что ваше решение будет хорошо работать в моем случае. – Joel

ответ

7

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

Вот короткое рекламное объявление о том, как я могу решить эту проблему:

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

Создать отдельную EmailLookup сущность которого @Id нормированная форма электронной почты (я только строчными все - технически неправильно, но сохраняет много боли, когда пользователи случайно используют [email protected]). Моя 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; 
    } 
} 

Существует также (не нормированные) электронной почты поле в моем User лица, которые я использую при отправке исходящих сообщений электронной почты (сохранить дело только в случае, если это важно для кого-то). Когда кто-то создает учетную запись с определенным электронным письмом, я загружаю/создаю EmailLookup и объекты пользователя по ключу в транзакции XG. Это гарантирует, что любой индивидуальный адрес электронной почты будет уникальным.

Та же стратегия применяется для любого другого уникального значения; facebook id, имя пользователя и т. д.

+1

Я могу ручаться за этот метод. Я рекомендую 100% 'long' автоматически генерируемые идентификаторы для всех« фактических »объектов, а также идентификаторы строк для усилителей согласованности/запросов, подобных этому –

+0

. В большинстве приложений есть вполне законные естественные ключи, и использование их часто может сэкономить для выполнения запросов, в пользу гораздо более эффективного сбора данных. Например, естественным ключом сущности 'page' является его URI. –

+1

Конечно, существует множество сущностей, для которых естественный ключ имеет смысл. Я утверждаю, что пользовательский объект OP не является одним из них :-) – stickfigure

3

Путь вокруг HRD's eventual consistency, заключается в использовании get вместо query. Чтобы это сделать, вам нужно генерировать естественные идентификаторы, например. генерировать идентификаторы, которые состоят из данных, которые вы получаете в запросе: электронная почта и имя пользователя.

С get в HRD имеет сильную согласованность, вы сможете надежно проверить, существует ли пользователь.

Например читаемый естественный ID будет:

String naturalUserId = userEmail + "-" + userName; 

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

+1

Мне нравится простота этого решения, но мне не нравится идея изменить ключ, если изменится адрес электронной почты или имя пользователя. – Joel

-1

Вы также можете включить транзакции между группами (см. https://developers.google.com/appengine/docs/java/datastore/overview#Cross_Group_Transactions), а затем в одной транзакции ищите пользователя и создайте новый, если это поможет.

+1

Из этого документа: «Подобно транзакциям с одной группой вы не можете выполнять запрос, не являющийся предком, в транзакции XG». –

+0

Ну, тогда есть проблема. Возможно, попробуйте выполнить запрос перед транзакцией, сохранить процесс, а затем за пределами транзакции выполнить другой запрос, чтобы проверить, есть ли только один результат желаемого типа? Однако стоит затратить гораздо больше времени на процессор, хотя это двойная проверка, когда маловероятно, чтобы люди заполнили один и тот же адрес электронной почты. Кроме того, это решение выглядит довольно хорошо: http://stackoverflow.com/a/10032511/473180 – themarketka

+0

Кроме того, двойная проверка также не может быть последовательной, поэтому вы все равно можете иметь дубликаты, не зная. В принципе, запросы, не относящиеся к предкам, никогда не могут использоваться для проверки согласованности с HRD. –

-1

Рекомендовать избегать индексированного поля и запроса, если у вас нет других применений. Вот что я сделал раньше (Python), используя key_name (поскольку идентификаторы объектов должны быть ints). Простота использования ключа или имени для других объектов, которые должны быть связаны с пользователем:

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

Это не работает, так как другой пользователь может вставить новое имя пользователя записи == usernameLower непосредственно перед помещением. – supercobra

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