2013-09-01 66 views
3

我正在尋找使用Firebase作爲Web應用用戶數據的數據存儲。我當前的想法是使用他們加入時的時間戳來存儲每個用戶的數據,作爲引用該用戶數據的關鍵字。這種方案的優點是,它是一種簡單的方法來爲用戶分配唯一的整數ID,並簡化用戶按時間排序。Firebase如何以原子方式防止重複條目

然而,不利的一面是,如果兩個「添加用戶」請求提交相同的數據,應用程序將愉快地添加兩個單獨的條目,這是不合理的。我可以洗牌(我開始認爲我應該使用電子郵件作爲關鍵,並通過加入數據來確定優先級,而不是我目前的方案),但是假設我不想。有什麼辦法可以防止重複的數據?

簡易方法很可能只是做一些事情,如:

if(!searchFirebaseForUser(data)) { 
    addUser(data); 
} 

但是,這絕對是一個競爭條件;對於兩個請求都很容易查詢和找到數據庫中的任何用戶,並且兩者都會添加。我希望在交易中這樣做,但Firebase交易支持似乎並不涵蓋這種情況。有什麼辦法可以解決這個問題嗎?

回答

6

你可能要使用的用戶名或電子郵件地址作爲重點,並嘗試以原子寫入該位置。

以下是transaction函數參考中的相關代碼示例。在這種情況下,我們使用wilma作爲用戶的關鍵。

// Try to create a user for wilma, but only if the user id 'wilma' isn't already taken. 
var wilmaRef = new Firebase('https://SampleChat.firebaseIO-demo.com/users/wilma'); 
wilmaRef.transaction(function(currentData) { 
    if (currentData === null) { 
    return {name: {first: 'Wilma', last: 'Flintstone'} }; 
    } else { 
    console.log('User wilma already exists.'); 
    return; // Abort the transaction. 
    } 
}, function(error, committed, snapshot) { 
    if (error) 
    console.log('Transaction failed abnormally!', error); 
    else if (!committed) 
    console.log('We aborted the transaction (because wilma already exists).'); 
    else 
    console.log('User wilma added!'); 
    console.log('Wilma\'s data: ', snapshot.val()); 
}); 
0

您可以使用push自動生成按時間順序遞增的ID,即使它們在同一時間創建(它們中包含隨機組件),也不會與其他客戶端發生衝突。

例如:

var ref = new Firebase(URL); 
var record = ref.push(userInfo); 
console.log("User was assigned ID: " + record.name()); 
+1

這裏有兩件事:我不認爲這會解決我的問題,以防相同的用戶被添加兩次。我的問題是,我不希望兩個相同的條目僅因ID而不同。 – Retsam

+0

在附註上,我避免使用push的原因是因爲它提供非數字ID,因爲它必須與假定用戶ID是數字的其他組件良好地配合使用。 – Retsam

+1

啊,明白了。您需要使用.transaction()來分配唯一的ID。這裏有一個例子:https://gist.github.com/anantn/4323981 – Anant

4

安全規則不足以執行唯一性嗎?我不知道他們是不是原子。

{ 
    "rules": { 
     "users": { 
      "$username": { 
       ".write": "!data.exists()" 
      } 
     } 
    } 
} 
+0

這有助於防止我在用戶登錄時寫入重複記錄(當用戶手動點擊時,我在用戶表中創建記錄我的應用程序上的登錄按鈕)。我在Firebase文檔中關注瞭如何在不使用自定義登錄(如我的情況下爲Facebook)的情況下創建單獨的用戶表的頁面:https://www.firebase.com/docs/web/guide/user-auth .html#分區存儲 – Brett84c

+0

不起作用。如果已經寫了一些東西,這段代碼的作用是避免在用戶名內寫入。 –

-1

而不是定義在消防基礎數據庫中的規則,以防止重複條目,最簡單的方法是首先從消防基礎數據庫中獲取的所有數據,並將其與數據進行比較(新的數據),你想存儲,如果它與以前的數據匹配,然後再放棄存儲在數據庫中,否則存儲在database.check下面更清晰。

public class MainActivity extends AppCompatActivity { 
private static final String TAG = MainActivity.class.getSimpleName(); 
private BroadcastReceiver mRegistrationBroadcastReceiver; 
private TextView txtRegId, txtMessage; 
DatabaseReference databaseArtists; 
ListView listViewArtists; 
public static String regId; 
List<Artist> artistList; 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
txtRegId = (TextView) findViewById(R.id.regid); 
txtRegId.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View view) { 
      displayFirebaseRegId(); 
      boolean flag=false; 
      String tokenId=regId; 
      for(Artist a:artistList) 
      {Log.d("RAaz",a.getTokenId()+" "+tokenId); 
       if(a.getTokenId().equalsIgnoreCase(tokenId)) 
       { 
        flag=true; 
        Toast.makeText(MainActivity.this, "True", Toast.LENGTH_SHORT).show(); 
       } 
      } 
      if(flag) 
      { 
       Toast.makeText(MainActivity.this, "User Already Exists", Toast.LENGTH_SHORT).show(); 
      } 
      else { 
       addArtist(); 
      } 
     } 
    }); 
    mRegistrationBroadcastReceiver = new BroadcastReceiver() { 
     @Override 
     public void onReceive(Context context, Intent intent) { 
      // checking for type intent filter 
      if (intent.getAction().equals(Config.REGISTRATION_COMPLETE)) { 
       // gcm successfully registered 
       // now subscribe to `global` topic to receive app wide notifications 
       FirebaseMessaging.getInstance().subscribeToTopic(Config.TOPIC_GLOBAL); 
       displayFirebaseRegId(); 
      } else if (intent.getAction().equals(Config.PUSH_NOTIFICATION)) { 
       // new push notification is received 
       String message = intent.getStringExtra("message"); 
       Toast.makeText(getApplicationContext(), "Push notification: " + message, Toast.LENGTH_LONG).show(); 
       txtMessage.setText(message); 
      } 
     } 
    }; 
    displayFirebaseRegId(); 
    databaseArtists = FirebaseDatabase.getInstance().getReference("artist"); 
    artistList = new ArrayList<>();} 

下面的代碼是用於將數據添加到火力

private void addArtist() { 
    String name = "User"; 
    String genre = regId; 
    if (!TextUtils.isEmpty(name)) { 
     String id = databaseArtists.push().getKey(); 
     Artist artist = new Artist(id,genre,name); 
     databaseArtists.child(id).setValue(artist); 
     Toast.makeText(this, "Artist Added", Toast.LENGTH_SHORT).show(); 
    } else { 
     Toast.makeText(this, "Please enter name", Toast.LENGTH_SHORT).show(); 
    } 
} 

使用在onStart從火力數據庫中獲取的細節

protected void onStart() { 
    super.onStart(); 
    Toast.makeText(this, "On Start", Toast.LENGTH_SHORT).show(); 
    databaseArtists.addValueEventListener(new ValueEventListener() { 
     @Override 
     public void onDataChange(DataSnapshot dataSnapshot) { 
      artistList.clear(); 
      for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) { 
       Artist artist = dataSnapshot1.getValue(Artist.class); 
       artistList.add(artist); 
      } 
     } 
     @Override 
     public void onCancelled(DatabaseError databaseError) { 
     } 
    }); 
} 

最後加POJO類

public class Artist { 
private String artistId; 
private String tokenId; 
private String roleName; 

public Artist() { 
} 

public Artist(String artistId, String tokenId, String roleName) { 
    this.artistId = artistId; 
    this.tokenId = tokenId; 
    this.roleName = roleName; 
} 

public String getArtistId() { 
    return artistId; 
} 

public void setArtistId(String artistId) { 
    this.artistId = artistId; 
} 

public String getTokenId() { 
    return tokenId; 
} 

public void setTokenId(String tokenId) { 
    this.tokenId = tokenId; 
} 

public String getRoleName() { 
    return roleName; 
} 

public void setRoleName(String roleName) { 
    this.roleName = roleName; 
} 

}

+1

這個答案的問題在於檢查和插入操作不是原子的 - 當兩個用戶決定在大致相同的時間選擇相同的名字時存在競爭條件。 假設兩個用戶同時執行檢查//if(a.getTokenId(.equalsIgnoreCase(tokenId))//,它將爲兩個用戶返回false。因爲這兩個用戶都沒有將他們的tokenId插入到數據存儲中。 因此,兩個用戶的插入邏輯將被執行,最後一個執行插入的人將獲勝。 –