Начать
Если вы еще не настроили приложение и не получили доступ к базе данных, сначала ознакомьтесь с руководством 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-бит)
- Числа с плавающей запятой двойной точности
- Логические значения
- Строки
- Векторы вариантов
- Сопоставление строк с вариантами
Using SetValue() in this way overwrites data at the specified location, including any child nodes. However, you can still update a child without rewriting the entire object. If you want to allow users to update their profiles you could update the username as follows:
dbref.Child("users").Child(userId).Child("username").SetValue(name);
Добавить к списку данных
Use the PushChild() method to append data to a list in multiuser applications. The PushChild() method generates a unique key every time a new child is added to the specified Firebase reference. By using these auto-generated keys for each new element in the list, several clients can add children to the same location at the same time without write conflicts. The unique key generated by PushChild() is based on a timestamp, so list items are automatically ordered chronologically.
Вы можете использовать ссылку на новые данные, возвращаемые методом 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() для ссылки на местоположение этих данных.
Удаление также можно выполнить, указав в качестве значения для другой операции записи, например SetValue() или UpdateChildren() , Variant null . Этот метод можно использовать с 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 синхронизирует эти данные с удаленными серверами баз данных и с другими клиентами, используя принцип «максимальных усилий».
В результате все операции записи в базу данных немедленно запускают локальные события, еще до того, как данные будут записаны на сервер. Это означает, что ваше приложение остается отзывчивым независимо от задержки сети или качества соединения.
После восстановления соединения ваше приложение получает соответствующий набор событий, благодаря чему клиент синхронизируется с текущим состоянием сервера, без необходимости написания какого-либо пользовательского кода.