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 elementu DatabaseReference

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

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

Zapisywanie danych

Dane w bazie danych Firebase Realtime Database można zapisywać na 4 sposoby:

Metoda Typowe zastosowania
SetValue() Zapisywanie lub zastępowanie danych w zdefiniowanej ścieżce, np. users/<user-id>/<username>.
PushChild() Dodawanie do listy danych. Za każdym razem, gdy wywołujesz Push(), Firebase generuje unikalny klucz, który może też służyć jako unikalny identyfikator, np. user-scores/<user-id>/<unique-score-id>.
UpdateChildren() Aktualizowanie niektórych kluczy w zdefiniowanej ścieżce bez zastępowania wszystkich danych.
RunTransaction() Aktualizowanie złożonych danych, które mogą zostać uszkodzone przez jednoczesne aktualizacje.

Zapisywanie, aktualizowanie i usuwanie danych w odniesieniu

Podstawowe operacje zapisu

W przypadku podstawowych operacji zapisu możesz użyć SetValue(), aby zapisać dane w określonym odniesieniu, zastępując wszystkie istniejące dane w tej ścieżce. Za pomocą tej metody możesz przekazywać typy akceptowane przez JSON za pomocą typu Variant, który obsługuje:

  • Null (powoduje usunięcie danych)
  • Liczby całkowite (64-bitowe)
  • Liczby zmiennoprzecinkowe podwójnej precyzji
  • Wartości logiczne
  • Ciągi znaków
  • Wektory wariantów
  • Mapy ciągów znaków do wariantów

Użycie SetValue() w ten sposób powoduje zastąpienie danych w określonej lokalizacji, w tym wszystkich węzłów podrzędnych. Nadal jednak możesz zaktualizować element podrzędny bez ponownego zapisywania całego obiektu. Jeśli chcesz umożliwić użytkownikom aktualizowanie profili, możesz zaktualizować nazwę użytkownika w ten sposób:

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

Dołączanie do listy danych

Aby dołączyć dane do listy w aplikacjach wielodostępnych, użyj metody PushChild(). Metoda PushChild() generuje unikalny klucz za każdym razem, gdy do określonego odniesienia Firebase dodawany jest nowy element podrzędny. Dzięki używaniu tych automatycznie generowanych kluczy dla każdego nowego elementu na liście kilku klientów może jednocześnie dodawać elementy podrzędne do tej samej lokalizacji bez konfliktów zapisu. Unikalny klucz generowany przez PushChild() jest oparty na sygnaturze czasowej, więc elementy listy są automatycznie porządkowane chronologicznie.

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

Aktualizowanie określonych pól

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

Podczas wywoływania UpdateChildren() możesz aktualizować wartości elementów podrzędnych niższego poziomu, określając ścieżkę do klucza. Jeśli dane są przechowywane w kilku lokalizacjach, aby lepiej skalować , możesz zaktualizować wszystkie instancje tych danych za pomocą zwielokrotnienia wyjściowego danych. Na przykład gra może mieć klasę LeaderboardEntry w takiej postaci:

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

 public:
  LeaderboardEntry() {
  }

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

  std::map&ltstd::string, Object&gt ToMap() {
    std::map&ltstring, Variant&gt result = new std::map&ltstring, Variant&gt();
    result["uid"] = Variant(uid);
    result["score"] = Variant(score);

    return result;
  }
}

Aby utworzyć LeaderboardEntry i jednocześnie zaktualizować go w najnowszym kanale wyników oraz na liście 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&ltstd::string, Variant&gt entryValues = entry.ToMap();

  std::map&ltstring, Variant&gt childUpdates = new std::map&ltstring, Variant&gt();
  childUpdates["/scores/" + key] = entryValues;
  childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

  dbref.UpdateChildren(childUpdates);
}

W tym przykładzie użyto PushChild(), aby utworzyć wpis w węźle zawierającym wpisy wszystkich użytkowników w /scores/$key i jednocześnie pobrać klucz za pomocą key(). Klucz można następnie użyć do utworzenia drugiego wpisu w wynikach użytkownika w /user-scores/$userid/$key.

Za pomocą tych ścieżek możesz jednocześnie aktualizować wiele lokalizacji w drzewie JSON za pomocą jednego wywołania UpdateChildren(), tak jak w tym przykładzie, który tworzy nowy wpis w obu lokalizacjach. Jednoczesne aktualizacje wykonane w ten sposób są niepodzielne: albo wszystkie aktualizacje się powiodą, albo wszystkie się nie powiodą.

Usuwanie danych

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

Możesz też usunąć dane, określając null Variant jako wartość innej operacji zapisu, np. SetValue() lub UpdateChildren(). Możesz użyć tej techniki z UpdateChildren(), aby usunąć kilka elementów podrzędnych za pomocą jednego wywołania interfejsu API.

Dowiedz się, kiedy Twoje dane zostaną zatwierdzone.

Aby dowiedzieć się, kiedy Twoje dane zostaną zatwierdzone na serwerze Firebase Realtime Database, sprawdź czy wynik Future jest pozytywny.

Zapisywanie danych jako transakcji

Podczas pracy z danymi, które mogą zostać uszkodzone przez jednoczesne modyfikacje, np. liczniki przyrostowe, możesz użyć operacji transakcji. W tej operacji podajesz funkcję DoTransaction. Ta funkcja aktualizacji przyjmuje bieżący stan danych jako argument i zwraca nowy stan, który chcesz zapisać. Jeśli inny klient zapisze dane w tej lokalizacji, zanim nowa wartość zostanie zapisana, funkcja aktualizacji zostanie ponownie wywołana z nową bieżącą wartością, a zapis zostanie ponowiony.

Na przykład w grze możesz umożliwić użytkownikom aktualizowanie tabeli wyników za pomocą 5 najlepszych wyników:

void AddScoreToLeaders(std::string email,
                       long score,
                       DatabaseReference leaderBoardRef) {
  leaderBoardRef.RunTransaction([](firebase::database::MutableData* mutableData) {
    if (mutableData.children_count() &gt= MaxScores) {
      long minScore = LONG_MAX;
      MutableData *minVal = null;
      std::vector&ltMutableData&gt children = mutableData.children();
      std::vector&ltMutableData&gt::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 &lt minScore) {
          minScore = childScore;
          minVal = &amp*it;
        }
      }
      if (minScore &gt 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&ltstd::string, Variant&gt newScoreMap =
      new std::map&ltstd::string, Variant&gt();
    newScoreMap["score"] = score;
    newScoreMap["email"] = email;
    children.Add(newScoreMap);
    mutableData->set_value(children);
    return kTransactionResultSuccess;
  });
}

Użycie transakcji zapobiega nieprawidłowościom w tabeli wyników, jeśli kilku użytkowników jednocześnie zapisuje wyniki lub klient miał nieaktualne dane. Jeśli transakcja zostanie odrzucona, serwer zwróci bieżącą wartość do klienta, który ponownie uruchomi transakcję z zaktualizowaną wartością. Powtarza się to, dopóki transakcja nie zostanie zaakceptowana lub nie zostanie wykonana zbyt duża liczba prób.

Zapisywanie danych offline

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

Każdy klient połączony z bazą danych Firebase utrzymuje własną wewnętrzną wersję aktywnych danych. Gdy dane są zapisywane, najpierw są zapisywane w tej lokalnej wersji. Klient Firebase synchronizuje te dane z serwerami zdalnej bazy danych i innymi klientami na zasadzie „najlepszych starań”.

Dzięki temu wszystkie zapisy w bazie danych natychmiast wywołują zdarzenia lokalne, zanim jakiekolwiek dane zostaną zapisane na serwerze. Oznacza to, że Twoja aplikacja pozostaje responsywna niezależnie od opóźnienia sieci lub łączności.

Po ponownym nawiązaniu połączenia aplikacja otrzymuje odpowiedni zestaw zdarzeń, dzięki czemu klient synchronizuje się z bieżącym stanem serwera bez konieczności pisania kodu niestandardowego.

Następne kroki