2012-04-05 67 views
5

我有一個用Java編寫的GAE項目,我對HRD和一個我不確定如何解決的問題有一些想法。沒有祖先的Google App Engine HRD查詢

基本上我有我的系統中的用戶。用戶由用戶名,用戶名,電子郵件和密碼組成。每次我創建一個新用戶時,我都想檢查是否已經有一個用戶具有相同的用戶標識(不應該發生),用戶名或電子郵件。

用戶標識是關鍵,所以我認爲這樣做會是一致的。但是,當我執行查詢(並使用過濾器)來查找具有相同用戶名或電子郵件的可能用戶時,我無法確定結果是否一致。因此,如果某人在幾秒鐘前創建了一個具有相同用戶名或電子郵件的用戶,我可能無法在我的查詢中找到它。我知道祖先習慣解決這個問題,但如果我沒有祖先用於查詢呢?用戶沒有父母。

我很樂意聽到你對此的想法,以及在這些情況下被認爲是最佳實踐的東西。如果改變了任何東西,我正在使用Objectify for GAE。

+1

我問了一個問題,你可能會發現非常有用。 (http://stackoverflow.com/questions/6584435/how-can-i-create-two-unique-queriable-fields-for-a-gae-datastore-data-model)我也需要存儲唯一的信息爲我的用戶。就我而言,我需要爲每個用戶存儲唯一的電子郵件和唯一的用戶ID。這對HRD來說有點困難,但我的確找到了可靠的解決方案。 ... cont ... – RLH 2012-04-05 15:19:32

+0

我的情況唯一的問題是我的帳戶創建實現(請參閱我的答案)不能很好地擴展。在我的情況下,這是可以的,因爲我的GAE應用程序非常小,並且新用戶的緩慢滴滴(1或2個月)。此外,這些信息是Python,但代碼很簡單 - 您應該能夠重新 - 相對容易地將其翻譯成Java。 – RLH 2012-04-05 15:20:08

+0

@RLH感謝您的意見,總是很有興趣看到不同的解決方案,但我認爲您的解決方案在我的情況下不會奏效。 – Joel 2012-04-06 06:38:45

回答

7

我不會推薦你的用戶實體使用電子郵件或任何其他自然鍵。用戶更改他們的電子郵件地址,並且當有人更改他們的電子郵件時,您不希望最終重寫數據庫中的所有外鍵引用。

這裏是我如何解決這個問題的一個簡短的Blurb:

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

創建其@Id是電子郵件地址的歸一化形式(我只是小寫的一切單獨EmailLookup實體 - 在技術上不正確,但節省了當用戶不小心大寫[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; 
    } 
} 

也有在我的用戶的實體,這是我發送出站電子郵件時使用(未歸一化)的電子郵件字段(保留的情況下,以防萬一它很重要的人)。當某人使用特定電子郵件創建帳戶時,我通過XG交易中的密鑰加載/創建EmailLookup和用戶實體。這保證任何個人電子郵件地址將是唯一的。

同樣的策略適用於任何其他類型的唯一值;臉書ID,用戶名等

+1

我可以保證這種方法。對於所有「實際」實體,我推薦使用100%'long'自動生成的ID,併爲這樣的一致性/查詢增強器設置字符串ID – 2012-04-05 16:42:55

+0

在許多應用程序中使用完全合法的自然鍵,並且使用它們通常可以節省需求以進行查詢,以支持更高效的數據存儲獲取操作。例如,一個'page'實體的自然鍵是它的URI。 – 2012-04-08 00:06:14

+1

當然,有很多實體的自然鍵是有意義的。我只聲稱OP的用戶實體不是其中之一:-) – stickfigure 2012-04-08 06:35:21

3

圍繞HRD的一個方法eventual consistency,是用get而不是query。爲了能夠做到這一點,你需要生成自然ID,例如生成由您在請求中收到的數據組成的ID:電子郵件和用戶名。

由於HRD中的get具有較強的一致性,您將能夠可靠地檢查用戶是否已經存在。

例如可讀的天然ID是:

String naturalUserId = userEmail + "-" + userName; 

注:在實踐中的電子郵件是唯一的。所以這是一個很好的自然ID。無需爲其添加組成用戶名。

+1

我喜歡這個解決方案的簡單性,但我不喜歡在電子郵件或用戶名更改時不得不更改密鑰的想法。 – Joel 2012-04-06 06:40:26

-1

您也可以啓用跨組交易(請參閱https://developers.google.com/appengine/docs/java/datastore/overview#Cross_Group_Transactions),然後在一個事務中查找該用戶並創建一個新的(如果有幫助的話)。

+1

從該文檔中:「與單組事務類似,您無法在XG事務中執行非祖先查詢。」 – 2012-04-05 16:44:34

+0

那麼,有一個問題。也許在事務之前嘗試查詢,進程保存,然後在事務之外執行另一個查詢來檢查是否只有一種期望類型的結果?但是,如果人們不太可能填寫相同的電子郵件地址,則必須花費更多的CPU時間,這種雙重檢查。此外這個解決方案看起來很好:http://stackoverflow.com/a/10032511/473180 – themarketka 2012-04-06 11:25:01

+0

此外,雙重檢查也不保證是一致的,所以你可以仍然有重複而不知道。基本上,非祖先查詢不能用於檢查與HRD的一致性。 – 2012-04-06 14:33:41

-1

建議避免索引字段和查詢,除非您有其他用途。這是我之前使用key_name完成的(Python)(因爲實體ID需要是整數)。易於使用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) 
+0

這不起作用,因爲另一個用戶可以在put之前插入一個新的記錄username == usernameLower。 – supercobra 2012-04-07 20:46:23