Начать
Если вы еще не настроили свое приложение и не получили доступ к базе данных, сначала ознакомьтесь с руководством Get Started
.
Получить ссылку на базу данных
Для записи данных в базу данных вам необходим экземпляр DatabaseReference
:
// Get the root reference location of the database. firebase::database::DatabaseReference dbref = database->GetReference();
Сохранение данных
Существует четыре способа записи данных в Firebase Realtime Database :
Метод | Распространенные применения |
---|---|
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()
чтобы получить значение автоматически сгенерированного ключа дочернего элемента или задать данные для него. Вызов метода GetKey()
для ссылки PushChild()
возвращает значение автоматически сгенерированного ключа.
Обновить определенные поля
Для одновременной записи в определенные дочерние узлы узла без перезаписи других дочерних узлов используйте метод UpdateChildren()
.
При вызове метода UpdateChildren()
можно обновить значения дочерних элементов нижнего уровня, указав путь к ключу. Если данные хранятся в нескольких местах для лучшего масштабирования, можно обновить все экземпляры этих данных, используя функцию data fan-out . Например, в игре может быть класс 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()
для удаления нескольких дочерних элементов за один вызов API.
Знайте, когда ваши данные передаются.
Чтобы узнать, когда ваши данные будут переданы на сервер Firebase Realtime Database , проверьте результат Future на предмет успешности.
Сохранить данные как транзакции
При работе с данными, которые могут быть повреждены одновременными изменениями, такими как инкрементные счётчики, можно использовать транзакционную операцию . Эта операция выполняется с помощью функции 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 синхронизирует эти данные с удаленными серверами баз данных и другими клиентами по принципу «максимальных усилий».
В результате все записи в базу данных запускают локальные события немедленно, ещё до того, как данные будут записаны на сервер. Это означает, что ваше приложение остаётся отзывчивым независимо от задержек в сети или проблем с подключением.
После восстановления соединения ваше приложение получает соответствующий набор событий, чтобы клиент синхронизировался с текущим состоянием сервера без необходимости написания какого-либо пользовательского кода.