Zapisywanie danych w bazie danych Firebase Realtime Database dla C++

Zaczynaj

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

Uzyskaj odniesienie 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

Istnieją cztery metody zapisywania danych w bazie danych Firebase Realtime Database:

metoda Typowe zastosowania
SetValue() Zapisz lub zamień dane w zdefiniowanej ścieżce, takiej jak 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 również użyć jako unikalnego identyfikatora, takiego jak user-scores/<user-id>/<unique-score-id> .
UpdateChildren() Zaktualizuj niektóre klucze dla określonej ścieżki bez zastępowania wszystkich danych.
RunTransaction() Aktualizuj złożone dane, które mogą zostać uszkodzone przez równoczesne aktualizacje.

Zapisuj, aktualizuj lub usuwaj dane w odniesieniu

Podstawowe operacje zapisu

W przypadku podstawowych operacji zapisu można użyć SetValue() w celu zapisania danych w określonym odwołaniu, zastępując wszelkie istniejące dane w tej ścieżce. Możesz użyć tej metody, aby przekazać typy akceptowane przez JSON przez typ Variant, który obsługuje:

  • Null (to usuwa dane)
  • Liczby całkowite (64-bitowe)
  • Liczby zmiennoprzecinkowe podwójnej precyzji
  • Wartości logiczne
  • Smyczki
  • Wektory wariantów
  • Mapy ciągów na warianty

Użycie metody SetValue() w ten sposób powoduje nadpisanie danych w określonej lokalizacji, łącznie ze wszystkimi węzłami podrzędnymi. Jednak nadal możesz zaktualizować element podrzędny bez przepisywania całego obiektu. Jeśli chcesz umożliwić użytkownikom aktualizację swoich profili, możesz zaktualizować nazwę użytkownika w następujący sposób:

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

Dołącz do listy danych

Użyj metody PushChild() , aby dołączyć dane do listy w aplikacjach obsługujących wielu użytkowników. Metoda PushChild() generuje unikalny klucz za każdym razem, gdy do określonego odniesienia Firebase dodawany jest nowy element podrzędny. Używając automatycznie generowanych kluczy dla każdego nowego elementu na liście, kilku klientów może dodać elementy podrzędne do tej samej lokalizacji w tym samym czasie, bez konfliktów zapisu. Unikalny klucz generowany przez PushChild() opiera się na znaczniku czasu, 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 dziecka lub ustawić dane dla dziecka. Wywołanie funkcji GetKey() w odniesieniu do odwołania PushChild() zwraca wartość automatycznie wygenerowanego klucza.

Zaktualizuj określone pola

Aby jednocześnie pisać do określonych węzłów podrzędnych węzła bez nadpisywania innych węzłów podrzędnych, użyj metody UpdateChildren() .

Wywołując funkcję UpdateChildren() , można zaktualizować wartości podrzędne niższego poziomu, określając ścieżkę do klucza. Jeśli dane są przechowywane w wielu lokalizacjach w celu lepszego skalowania, możesz zaktualizować wszystkie wystąpienia tych danych, korzystając z rozdzielania danych . Na przykład gra może mieć klasę LeaderboardEntry taką jak ta:

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ć wpis LeaderboardEntry i jednocześnie zaktualizować go do najnowszego źródła wyników i własnej listy wyników użytkownika, gra używa następującego 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 zastosowano funkcję PushChild() do utworzenia wpisu w węźle zawierającego wpisy dla wszystkich użytkowników w /scores/$key i jednoczesnego pobrania klucza za pomocą key() . Klucza można następnie użyć do utworzenia drugiego wpisu w wynikach użytkownika pod /user-scores/$userid/$key .

Korzystając z tych ścieżek, można wykonywać jednoczesne aktualizacje wielu lokalizacji w drzewie JSON za pomocą jednego wywołania funkcji UpdateChildren() , tak jak w tym przykładzie tworzony jest nowy wpis w obu lokalizacjach. Jednoczesne aktualizacje wykonane w ten sposób mają charakter niepodzielny: albo wszystkie aktualizacje się powiodą, albo wszystkie aktualizacje nie powiodą się.

Usunąć dane

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

Można również usunąć, określając null Variant jako wartość innej operacji zapisu, takiej jak SetValue() lub UpdateChildren() . Możesz użyć tej techniki z UpdateChildren() , aby usunąć wiele dzieci w jednym wywołaniu API.

Dowiedz się, kiedy Twoje dane są przekazywane.

Aby dowiedzieć się, kiedy Twoje dane zostaną zapisane na serwerze bazy danych Firebase Realtime Database, sprawdź wynik Przyszłość .

Zapisz dane jako transakcje

Podczas pracy z danymi, które mogą zostać uszkodzone przez jednoczesne modyfikacje, takie jak liczniki przyrostowe, można użyć operacji transakcyjnej . Nadajesz tej operacji funkcję DoTransaction . Ta funkcja aktualizacji przyjmuje bieżący stan danych jako argument i zwraca nowy żądany stan, który chcesz zapisać. Jeśli inny klient zapisuje do lokalizacji przed pomyślnym zapisaniem nowej wartości, funkcja aktualizacji zostanie wywołana ponownie z nową bieżącą wartością i ponowienie próby zapisu.

Na przykład w grze możesz pozwolić użytkownikom na aktualizację tabeli liderów z pięcioma 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;
  });
}

Korzystanie z transakcji zapobiega błędom w tabeli liderów, jeśli wielu użytkowników rejestruje wyniki w tym samym czasie lub klient ma nieaktualne dane. Jeżeli transakcja zostanie odrzucona, serwer zwraca aktualną wartość klientowi, który ponownie uruchamia transakcję ze zaktualizowaną wartością. Powtarza się to do momentu zaakceptowania transakcji lub podjęcia zbyt wielu prób.

Zapisuj dane w trybie offline

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

Każdy klient podłączony do bazy danych Firebase utrzymuje własną wewnętrzną wersję wszelkich aktywnych danych. Kiedy dane są zapisywane, są najpierw zapisywane w tej wersji lokalnej. Następnie klient Firebase synchronizuje te dane ze zdalnymi serwerami baz danych i innymi klientami, dokładając wszelkich starań.

W rezultacie wszystkie zapisy do bazy danych natychmiast wyzwalają 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 odbiera odpowiedni zestaw zdarzeń, dzięki czemu klient synchronizuje się z bieżącym stanem serwera bez konieczności pisania żadnego niestandardowego kodu.

Następne kroki