Google is committed to advancing racial equity for Black communities. See how.
Эта страница была переведа с помощью Cloud Translation API.
Switch to English

Сохранение данных с помощью базы данных Firebase Realtime для C ++

Начать

Сначала Get Started руководством по Get Started если вы еще не настроили свое приложение и не получили доступ к базе данных.

Получить DatabaseReference

Чтобы записать данные в базу данных, вам нужен экземпляр DatabaseReference :

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

Сохранение данных

Существует четыре способа записи данных в базу данных Firebase Realtime:

метод Общее использование
SetValue() Записывайте или заменяйте данные по определенному пути, например users/<user-id>/<username> .
PushChild() Добавить в список данных. Каждый раз, когда вы вызываете Push() , Firebase генерирует уникальный ключ, который также может использоваться в качестве уникального идентификатора, например user-scores/<user-id>/<unique-score-id> .
UpdateChildren() Обновите некоторые ключи для определенного пути, не заменяя все данные.
RunTransaction() Обновите сложные данные, которые могут быть повреждены при одновременном обновлении.

Написать, обновить или удалить данные по ссылке

Основные операции записи

Для основных операций записи вы можете использовать SetValue() чтобы сохранить данные по указанной ссылке, заменив любые существующие данные по этому пути. Вы можете использовать этот метод для передачи типов, принятых JSON, через тип Variant, который поддерживает:

  • Null (это удаляет данные)
  • Целые числа (64-разрядные)
  • Числа с плавающей запятой двойной точности
  • Булевы
  • Струны
  • Векторы вариантов
  • Карты строк в варианты

Использование SetValue() таким образом перезаписывает данные в указанном месте, включая любые дочерние узлы. Однако вы все равно можете обновить дочерний элемент, не переписывая весь объект. Если вы хотите разрешить пользователям обновлять свои профили, вы можете обновить имя пользователя следующим образом:

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

Добавить в список данных

Используйте метод PushChild() для добавления данных в список в многопользовательских приложениях. Метод PushChild() генерирует уникальный ключ каждый раз, когда в указанную ссылку Firebase добавляется новый дочерний элемент. Используя эти автоматически сгенерированные ключи для каждого нового элемента в списке, несколько клиентов могут одновременно добавлять дочерние элементы в одно и то же место без конфликтов записи. Уникальный ключ, сгенерированный PushChild() , основан на PushChild() времени, поэтому элементы списка автоматически упорядочиваются в хронологическом порядке.

Вы можете использовать ссылку на новые данные, возвращаемые методом PushChild() чтобы получить значение автоматически сгенерированного дочернего ключа или установить данные для дочернего элемента. Вызов GetKey() для PushChild() возвращает значение автоматически сгенерированного ключа.

Обновить определенные поля

Для одновременной записи в определенные дочерние узлы без перезаписи других дочерних узлов используйте метод 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 .

Используя эти пути, вы можете выполнять одновременное обновление нескольких местоположений в дереве JSON с помощью одного вызова UpdateChildren() , например, как в этом примере создается новая запись в обоих местоположениях. Одновременные обновления, выполненные таким способом, являются атомарными: либо все обновления завершаются успешно, либо все обновления не выполняются

Удалить данные

Самый простой способ удалить данные - вызвать RemoveValue() для ссылки на местоположение этих данных.

Вы также можете удалить, указав null Variant в качестве значения для другой операции записи, такой как SetValue() или UpdateChildren() . Вы можете использовать эту технику с UpdateChildren() для удаления нескольких дочерних UpdateChildren() за один вызов API.

Знайте, когда ваши данные передаются.

Чтобы узнать, когда ваши данные передаются на сервер базы данных Firebase Realtime, проверьте результат на будущее .

Сохранить данные как транзакции

При работе с данными, которые могут быть повреждены одновременными изменениями, такими как инкрементные счетчики, вы можете использовать операцию транзакции . Вы даете этой операции функцию 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 синхронизирует эти данные с удаленными серверами баз данных и с другими клиентами на основе «максимальных усилий».

В результате все записи в базу данных немедленно инициируют локальные события, прежде чем любые данные будут записаны на сервер. Это означает, что ваше приложение остается отзывчивым независимо от задержки в сети или подключения.

Как только связь восстановлена, ваше приложение получает соответствующий набор событий, так что клиент синхронизируется с текущим состоянием сервера без необходимости написания какого-либо пользовательского кода.

Следующие шаги