Google is committed to advancing racial equity for Black communities. See how.
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

使用Firebase C ++實時數據庫保存數據

開始使用

如果尚未設置應用程序和訪問數據庫,請先參閱《 Get Started指南》。

獲取數據庫參考

要將數據寫入數據庫,您需要一個DatabaseReference實例:

    // Get the root reference location of the database.
    firebase::database::DatabaseReference dbref = database->GetReference();

保存數據

有四種方法可以將數據寫入Firebase實時數據庫:

方法常見用途
SetValue() 將數據寫入或替換到已定義的路徑,例如users/<user-id>/<username>
PushChild() 添加到數據列表。每次調用Push() ,Firebase都會生成一個唯一密鑰,該密鑰也可以用作唯一標識符,例如user-scores/<user-id>/<unique-score-id>
UpdateChildren() 在不替換所有數據的情況下,更新已定義路徑的某些鍵。
RunTransaction() 更新可能被並發更新破壞的複雜數據。

在參考處寫入,更新或刪除數據

基本寫操作

對於基本寫操作,可以使用SetValue()將數據保存到指定的引用,從而替換該路徑上的任何現有數據。您可以使用此方法通過支持以下內容的Variant類型傳遞JSON接受的類型:

  • 空(這將刪除數據)
  • 整數(64位)
  • 雙精度浮點數
  • 布爾值
  • 弦樂
  • 向量的變體
  • 字符串到變體的映射

以這種方式使用SetValue()覆蓋指定位置(包括所有子節點)上的數據。但是,您仍然可以更新子項而無需重寫整個對象。如果要允許用戶更新其個人資料,則可以按以下方式更新用戶名:

dbref.Child("users").Child(userId).Child("username").SetValue(name);

附加到數據列表

使用PushChild()方法將數據追加到多用戶應用程序中的列表中。每當將新的子項添加到指定的Firebase引用時, PushChild()方法都會生成一個唯一鍵。通過為列表中的每個新元素使用這些自動生成的鍵,多個客戶端可以同時將子代添加到同一位置,而不會發生寫衝突。 PushChild()生成的唯一鍵是基於時間戳的,因此列表項PushChild()時間順序自動排序。

您可以使用對PushChild()方法返回的新數據的引用,以獲取子項的自動生成鍵的值或為子項設置數據。在PushChild()引用上調用GetKey()返回自動生成的鍵的值。

更新特定字段

若要同時寫入節點的特定子節點而不覆蓋其他子節點,請使用UpdateChildren()方法。

調用UpdateChildren() ,可以通過指定鍵的路徑來更新較低級別的子值。如果數據存儲在多個位置以更好地擴展,則可以使用數據扇出更新該數據的所有實例。例如,一個遊戲可能具有如下的LeaderboardEntry類:

class LeaderboardEntry {
  std::string uid;
  int score = 0;

 public:
  LeaderboardEntry() {
  }

  LeaderboardEntry(std::string uid, int score) {
    this->uid = uid;
    this->score = score;
  }

  std::map<std::string, Object> ToMap() {
    std::map<string, Variant> result = new std::map<string, Variant>();
    result["uid"] = Variant(uid);
    result["score"] = Variant(score);

    return result;
  }
}

要創建一個LeaderboardEntry並同時將其更新為最近的得分供稿和用戶自己的得分列表,遊戲將使用以下代碼:

void WriteNewScore(std::string userId, int score) {
  // Create new entry at /user-scores/$userid/$scoreid and at
  // /leaderboard/$scoreid simultaneously
  std::string key = dbref.Child("scores").PushChild().GetKey();
  LeaderBoardEntry entry = new LeaderBoardEntry(userId, score);
  std::map<std::string, Variant> entryValues = entry.ToMap();

  std::map<string, Variant> childUpdates = new std::map<string, Variant>();
  childUpdates["/scores/" + key] = entryValues;
  childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

  dbref.UpdateChildren(childUpdates);
}

本示例使用PushChild()在節點中創建一個條目,該條目包含/scores/$key所有用戶的條目,並同時使用key()檢索密鑰。然後,該密鑰可用於在用戶分數中的/user-scores/$userid/$key創建第二個條目。

使用這些路徑,您可以通過一次調用UpdateChildren()來同時更新JSON樹中的多個位置,例如本示例在兩個位置中創建新條目的方式。通過這種方式進行的同時更新是原子性的:要么所有更新成功,要么所有更新失敗。

刪除資料

刪除數據的最簡單方法是在對數據位置的引用上調用RemoveValue()

您還可以通過將null Variant指定為另一個寫操作(例如SetValue()UpdateChildren()的值來刪除。您可以將此技術與UpdateChildren()以在單個API調用中刪除多個子級。

知道何時提交數據。

要知道何時將數據提交到Firebase實時數據庫服務器,請檢查“ 將來”結果是否成功。

將數據另存為交易

當使用可能被並發修改破壞的數據(例如增量計數器)時,可以使用事務操作 。您為此操作提供一個DoTransaction函數。此更新函數將數據的當前狀態作為參數,並返回要寫入的新的所需狀態。如果另一個客戶端在成功寫入新值之前寫入該位置,則將使用新的當前值再次調用更新函數,然後重試寫入。

例如,在遊戲中,您可以允許用戶更新得分最高的五個排行榜:

void AddScoreToLeaders(std::string email,
                       long score,
                       DatabaseReference leaderBoardRef) {
  leaderBoardRef.RunTransaction([](firebase::database::MutableData* mutableData) {
    if (mutableData.children_count() >= MaxScores) {
      long minScore = LONG_MAX;
      MutableData *minVal = null;
      std::vector<MutableData> children = mutableData.children();
      std::vector<MutableData>::iterator it;
      for (it = children.begin(); it != children.end(); ++it) {
        if (!it->value().is_map())
          continue;
        long childScore = (long)it->Child("score").value().int64_value();
        if (childScore < minScore) {
          minScore = childScore;
          minVal = &*it;
        }
      }
      if (minScore > score) {
        // The new score is lower than the existing 5 scores, abort.
        return kTransactionResultAbort;
      }

      // Remove the lowest score.
      children.Remove(minVal);
    }

    // Add the new high score.
    std::map<std::string, Variant> newScoreMap =
      new std::map<std::string, Variant>();
    newScoreMap["score"] = score;
    newScoreMap["email"] = email;
    children.Add(newScoreMap);
    mutableData->set_value(children);
    return kTransactionResultSuccess;
  });
}

如果多個用戶同時記錄分數或客戶端具有過時的數據,則使用事務可以防止排行榜出現錯誤。如果交易被拒絕,則服務器將當前值返回給客戶端,客戶端將使用更新後的值再次運行交易。重複此操作,直到接受交易或進行過多嘗試為止。

離線寫入數據

如果客戶端失去網絡連接,則您的應用將繼續正常運行。

連接到Firebase數據庫的每個客戶端都維護其自己的內部版本的任何活動數據。寫入數據後,首先將其寫入此本地版本。然後,Firebase客戶端將根據“最大努力”與遠程數據庫服務器和其他客戶端同步該數據。

結果,所有寫入數據庫的操作都會在任何數據寫入服務器之前立即觸發本地事件。這意味著您的應用程序始終保持響應狀態,無論網絡延遲或連接性如何。

重新建立連接後,您的應用將接收適當的事件集,以便客戶端與當前服務器狀態同步,而無需編寫任何自定義代碼。

下一步