使用適用於 C++ 的 Firebase 即時資料庫保存數據

開始使用

如果您尚未設定應用程式和存取資料庫,請先參閱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() 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 用戶端會「盡力」將該資料與遠端資料庫伺服器和其他用戶端同步。

因此,在將任何資料寫入伺服器之前,對資料庫的所有寫入都會立即觸發本機事件。這意味著無論網路延遲或連接如何,您的應用程式都保持回應。

重新建立連線後,您的應用程式會收到適當的事件集,以便用戶端與目前伺服器狀態同步,而無需編寫任何自訂程式碼。

下一步