เริ่มต้นใช้งาน
โปรดดูคู่มือ Get Started ก่อนหากคุณยังไม่ได้ตั้งค่าแอปและสิทธิ์เข้าถึงฐานข้อมูล
รับ DatabaseReference
หากต้องการเขียนข้อมูลลงในฐานข้อมูล คุณต้องมีอินสแตนซ์ของ DatabaseReference ดังนี้
// Get the root reference location of the database. firebase::database::DatabaseReference dbref = database->GetReference();
กำลังบันทึกข้อมูล
การเขียนข้อมูลลงใน Firebase Realtime Database มี 4 วิธีดังนี้
| วิธีการ | การใช้งานทั่วไป |
|---|---|
SetValue() |
เขียนหรือแทนที่ข้อมูลในเส้นทางที่กำหนด เช่น users/<user-id>/<username> |
PushChild() |
เพิ่มลงในรายการข้อมูล ทุกครั้งที่คุณเรียกใช้ Push() Firebase จะสร้างคีย์ที่ไม่ซ้ำกันซึ่งใช้เป็นตัวระบุที่ไม่ซ้ำกันได้ด้วย เช่น user-scores/<user-id>/<unique-score-id> |
UpdateChildren() |
อัปเดตคีย์บางรายการสำหรับเส้นทางที่กำหนดโดยไม่ต้องแทนที่ข้อมูลทั้งหมด |
RunTransaction() |
อัปเดตข้อมูลที่ซับซ้อนซึ่งอาจเสียหายจากการอัปเดตที่เกิดขึ้นพร้อมกัน |
เขียน อัปเดต หรือลบข้อมูลที่ข้อมูลอ้างอิง
การดำเนินการเขียนพื้นฐาน
สำหรับการดำเนินการเขียนพื้นฐาน คุณสามารถใช้ SetValue() เพื่อบันทึกข้อมูลไปยังการอ้างอิงที่ระบุ โดยแทนที่ข้อมูลที่มีอยู่ในเส้นทางนั้น คุณสามารถใช้วิธีนี้เพื่อส่งประเภทที่ JSON ยอมรับผ่านประเภท Variant ซึ่งรองรับสิ่งต่อไปนี้
- Null (ซึ่งจะลบข้อมูล)
- จำนวนเต็ม (64 บิต)
- ตัวเลขจุดลอยตัวที่มีความแม่นยำแบบ Double
- บูลีน
- สตริง
- เวกเตอร์ของตัวแปร
- Maps of strings to Variants
การใช้ SetValue() ในลักษณะนี้จะเขียนทับข้อมูลในตำแหน่งที่ระบุ รวมถึงโหนดย่อยทั้งหมด อย่างไรก็ตาม คุณยังคงอัปเดตออบเจ็กต์ย่อยได้โดยไม่ต้องเขียนออบเจ็กต์ทั้งหมดใหม่ หากต้องการอนุญาตให้ผู้ใช้อัปเดตโปรไฟล์ของตนเอง คุณสามารถอัปเดตชื่อผู้ใช้ได้ดังนี้
dbref.Child("users").Child(userId).Child("username").SetValue(name);
ต่อท้ายรายการข้อมูล
ใช้เมธอด PushChild() เพื่อเพิ่มข้อมูลต่อท้ายรายการในแอปพลิเคชันที่มีผู้ใช้หลายคน
เมธอด PushChild() จะสร้างคีย์ที่ไม่ซ้ำกันทุกครั้งที่มีการเพิ่มรายการย่อยใหม่ลงในข้อมูลอ้างอิง Firebase ที่ระบุ การใช้คีย์ที่สร้างขึ้นโดยอัตโนมัติเหล่านี้สำหรับองค์ประกอบใหม่แต่ละรายการในรายการช่วยให้ไคลเอ็นต์หลายรายสามารถเพิ่มองค์ประกอบย่อยไปยังตำแหน่งเดียวกันพร้อมกันได้โดยไม่มีข้อขัดแย้งในการเขียน คีย์ที่ไม่ซ้ำกันซึ่งสร้างโดย 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() พร้อมกัน จากนั้นจะใช้คีย์เพื่อสร้างรายการที่ 2 ในคะแนนของผู้ใช้ที่ /user-scores/$userid/$key ได้
เมื่อใช้เส้นทางเหล่านี้ คุณจะอัปเดตหลายตำแหน่งในโครงสร้าง JSON พร้อมกันได้ด้วยการเรียกใช้ UpdateChildren() เพียงครั้งเดียว เช่น วิธีที่ตัวอย่างนี้สร้างรายการใหม่ในทั้ง 2 ตำแหน่ง การอัปเดตพร้อมกันด้วยวิธีนี้จะเป็นแบบอะตอม กล่าวคือ การอัปเดตทั้งหมดจะสำเร็จหรือล้มเหลวทั้งหมด
ลบข้อมูล
วิธีที่ง่ายที่สุดในการลบข้อมูลคือการเรียกใช้ RemoveValue() ในการอ้างอิงตำแหน่งของข้อมูลนั้น
นอกจากนี้ คุณยังลบได้โดยระบุ null Variant เป็นค่าสำหรับการดำเนินการเขียนอื่นๆ เช่น SetValue() หรือ UpdateChildren() คุณสามารถใช้เทคนิคนี้กับ UpdateChildren() เพื่อลบรายการย่อยหลายรายการในการเรียก API ครั้งเดียว
รู้ว่าข้อมูลของคุณพร้อมใช้งานเมื่อใด
หากต้องการทราบว่าระบบส่งข้อมูลไปยังเซิร์ฟเวอร์ Firebase Realtime Database เมื่อใด ให้ตรวจสอบผลลัพธ์ Future เพื่อดูว่าสำเร็จหรือไม่
บันทึกข้อมูลเป็นธุรกรรม
เมื่อทำงานกับข้อมูลที่อาจเสียหายจากการแก้ไขที่เกิดขึ้นพร้อมกัน เช่น ตัวนับแบบเพิ่มทีละขั้น คุณสามารถใช้การดำเนินการธุรกรรมได้
คุณกำหนดฟังก์ชัน DoTransaction ให้กับการดำเนินการนี้ ฟังก์ชันอัปเดตนี้จะใช้สถานะปัจจุบันของข้อมูลเป็นอาร์กิวเมนต์และแสดงผลสถานะใหม่ที่ต้องการซึ่งคุณต้องการเขียน หากไคลเอ็นต์อื่นเขียนไปยังตำแหน่งก่อนที่จะเขียนค่าใหม่สำเร็จ ระบบจะเรียกใช้ฟังก์ชันการอัปเดตอีกครั้งด้วยค่าปัจจุบันใหม่ และจะลองเขียนอีกครั้ง
ตัวอย่างเช่น ในเกม คุณอาจอนุญาตให้ผู้ใช้อัปเดตลีดเดอร์บอร์ดด้วยคะแนนสูงสุด 5 อันดับแรกได้ ดังนี้
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 จะซิงค์ข้อมูลดังกล่าวกับเซิร์ฟเวอร์ฐานข้อมูลระยะไกลและไคลเอ็นต์อื่นๆ โดยใช้ความพยายามอย่างเต็มที่
ด้วยเหตุนี้ การเขียนทั้งหมดลงในฐานข้อมูลจะทริกเกอร์เหตุการณ์ในเครื่องทันทีก่อนที่จะมีการเขียนข้อมูลลงในเซิร์ฟเวอร์ ซึ่งหมายความว่าแอปจะยังคงตอบสนองได้ไม่ว่าเวลาในการตอบสนองของเครือข่ายหรือการเชื่อมต่อจะเป็นอย่างไร
เมื่อเชื่อมต่ออีกครั้งแล้ว แอปจะได้รับชุดเหตุการณ์ที่เหมาะสมเพื่อให้ไคลเอ็นต์ซิงค์กับสถานะเซิร์ฟเวอร์ปัจจุบัน โดยไม่ต้องเขียนโค้ดที่กำหนดเอง