Zapisywanie danych w Bazie danych czasu rzeczywistego Firebase dla C++

Rozpocznij

Jeśli nie masz jeszcze skonfigurowanej aplikacji i dostępu do bazy danych, najpierw zapoznaj się z przewodnikiem Get Started.

Pobieranie odniesienia do bazy danych

Aby zapisać dane w bazie danych, potrzebujesz instancji DatabaseReference:

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

Zapisywanie danych

Są 4 metody zapisywania danych w Bazie danych czasu rzeczywistego Firebase:

Metoda Typowe zastosowania
SetValue() Zapisz lub zastąp dane w określonej ścieżce, np. users/<user-id>/<username>.
PushChild() Dodaj do listy danych. Za każdym razem, gdy wywołujesz Push(), Firebase generuje unikalny klucz, którego można też używać jako unikalnego identyfikatora, np. user-scores/<user-id>/<unique-score-id>.
UpdateChildren() Zaktualizuj niektóre klucze dla zdefiniowanej ścieżki bez zastępowania wszystkich danych.
RunTransaction() Aktualizuj złożone dane, które mogą zostać uszkodzone w wyniku równoczesnych aktualizacji.

Zapisywanie, aktualizowanie i usuwanie danych w odwołaniach

Podstawowe operacje zapisu

W przypadku podstawowych operacji zapisu możesz użyć funkcji SetValue(), aby zapisać dane w określonym odwołaniu, zastępując w przypadku danej ścieżki wszelkie dotychczasowe dane. Możesz użyć tej metody, aby przekazywać typy akceptowane w formacie JSON za pomocą typu wariantu, który obsługuje:

  • Null (spowoduje to usunięcie danych)
  • Liczba całkowita (64-bitowa)
  • Liczba zmiennoprzecinkowa z podwójną precyzją
  • Wartość logiczna
  • Ciągi znaków
  • Wektory wariantów
  • Mapy ciągów znaków na warianty

Użycie tego parametru SetValue() spowoduje zastąpienie danych w określonej lokalizacji, w tym wszystkich węzłów podrzędnych. Mimo to można zaktualizować element podrzędny bez przepisywania całego obiektu. Jeśli chcesz zezwolić użytkownikom na aktualizowanie swoich profili, możesz zmienić nazwę użytkownika w ten sposób:

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

Dołącz do listy danych

Użyj metody PushChild(), aby dołączać dane do listy w aplikacjach, które mają wielu użytkowników. Metoda PushChild() generuje unikalny klucz za każdym razem, gdy do określonego odwołania Firebase jest dodawane nowe elementy podrzędne. Dzięki użyciu tych automatycznie generowanych kluczy dla każdego nowego elementu na liście kilku klientów może dodawać elementy podrzędne do tej samej lokalizacji w tym samym czasie bez konfliktów zapisu. Unikalny klucz wygenerowany przez funkcję PushChild() jest oparty na sygnaturze czasowej, więc elementy listy są automatycznie porządkowane chronologicznie.

Możesz użyć odwołania do nowych danych zwróconych przez metodę PushChild(), aby uzyskać wartość automatycznie wygenerowanego klucza wydawcy podrzędnego lub ustawić dane dla elementu podrzędnego. Wywołanie GetKey() w przypadku odwołania do PushChild() zwraca wartość klucza wygenerowanego automatycznie.

Zaktualizuj określone pola

Aby jednocześnie zapisywać dane w określonych węzłach podrzędnych węzła bez zastępowania innych węzłów podrzędnych, użyj metody UpdateChildren().

Gdy wywołujesz UpdateChildren(), możesz zaktualizować wartości podrzędne niższego poziomu, podając ścieżkę klucza. Jeśli dane są przechowywane w kilku lokalizacjach w celu zwiększenia ich skalowania, możesz zaktualizować wszystkie ich wystąpienia za pomocą przekazywania danych na zewnątrz. Na przykład gra może mieć klasę LeaderboardEntry taką:

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;
  }
}

Aby utworzyć LeaderboardEntry i jednocześnie zaktualizować go do najnowszego wyniku oraz własnej listy wyników użytkownika, gra używa tego kodu:

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);
}

W tym przykładzie użyto funkcji PushChild() do utworzenia w węźle wpisu zawierającego wpisy dla wszystkich użytkowników w organizacji /scores/$key oraz jednoczesnego pobierania klucza za pomocą key(). Za pomocą klucza można utworzyć drugi wpis w wynikach użytkownika w /user-scores/$userid/$key.

Za pomocą tych ścieżek możesz aktualizować wiele lokalizacji w drzewie JSON za pomocą jednego wywołania UpdateChildren(), na przykład w jaki sposób ten przykład tworzy nowy wpis w obu lokalizacjach. Równoczesne aktualizacje przeprowadzane w ten sposób są niepodzielne: albo wszystkie aktualizacje zakończą się sukcesem, albo wszystkie kończą się niepowodzeniem.

Usuń dane

Najprostszym sposobem usunięcia danych jest wywołanie metody RemoveValue() w odniesieniu do lokalizacji tych danych.

Usuwanie możesz też usuwać, określając null Variant jako wartość innej operacji zapisu, takiej jak SetValue() lub UpdateChildren(). Możesz użyć tej metody razem z UpdateChildren(), aby usunąć wiele elementów podrzędnych w jednym wywołaniu interfejsu API.

Kontroluj, kiedy dane są udostępniane.

Aby dowiedzieć się, kiedy dane są przekazywane do serwera Bazy danych czasu rzeczywistego Firebase, sprawdź wynik w polu Przyszły, aby odnieść sukces.

Zapisywanie danych jako transakcji

Podczas pracy z danymi, które mogą ulec uszkodzeniu w wyniku równoczesnych modyfikacji, takich jak liczniki przyrostowe, możesz użyć operacji transakcji. Nadaj tej operacji funkcję DoTransaction. Ta funkcja aktualizacji przyjmuje jako argument bieżący stan danych i zwraca nowy pożądany stan, który chcesz zapisać. Jeśli inny klient zapisze dane w lokalizacji przed zapisaniem nowej wartości, funkcja aktualizacji zostanie wywołana ponownie z nową bieżącą wartością, a zapis jest ponawiany.

Na przykład w grze można zezwolić użytkownikom na aktualizowanie tabeli z 5 najwyższymi wynikami:

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;
  });
}

Użycie transakcji zapobiega nieprawidłowej tablicy wyników, jeśli w tym samym czasie wielu użytkowników rejestruje swoje wyniki lub klient ma nieaktualne dane. W przypadku odrzucenia transakcji serwer zwraca bieżącą wartość klientowi, który uruchamia transakcję ponownie ze zaktualizowaną wartością. Powtarza się, dopóki transakcja nie zostanie zaakceptowana lub nie dojdzie do zbyt wielu prób.

Zapisuj dane offline

Jeśli klient utraci połączenie sieciowe, aplikacja będzie nadal działać prawidłowo.

Każdy klient połączony z bazą danych Firebase zachowuje własną, wewnętrzną wersję wszystkich aktywnych danych. Podczas zapisywania danych są one najpierw zapisywane w tej wersji lokalnej. Następnie klient Firebase synchronizuje te dane ze zdalnymi serwerami baz danych i innymi klientami w ramach najlepszych starań.

W rezultacie wszystkie zapisy w bazie danych natychmiast uruchamiają zdarzenia lokalne, zanim jakiekolwiek dane zostaną zapisane na serwerze. Oznacza to, że aplikacja pozostaje elastyczna bez względu na opóźnienia sieciowe czy połączenie.

Gdy połączenie zostanie przywrócone, aplikacja otrzyma odpowiedni zestaw zdarzeń, aby klient mógł synchronizować się z obecnym stanem serwera bez konieczności pisania niestandardowego kodu.

Następne kroki