เริ่มต้นใช้งาน
โปรดดูคู่มือGet Started ก่อนหากคุณยังไม่ได้
ตั้งค่าแอปและสิทธิ์เข้าถึงฐานข้อมูล
รับ DatabaseReference
หากต้องการเขียนข้อมูลลงในฐานข้อมูล คุณต้องมีอินสแตนซ์ของ DatabaseReference ดังนี้
// Get the root reference location of the database. firebase::database::DatabaseReference dbref = database->GetReference();
การบันทึกข้อมูล
มี 4 วิธีในการเขียนข้อมูลลงใน 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() คุณสามารถอัปเดตค่าลูกระดับล่างได้โดยการระบุเส้นทางสำหรับคีย์ หากจัดเก็บข้อมูลไว้ในหลายตำแหน่งเพื่อปรับขนาดให้ดีขึ้น คุณสามารถอัปเดตอินสแตนซ์ทั้งหมดของข้อมูลนั้นได้โดยใช้การกระจายข้อมูล ตัวอย่างเช่น เกมอาจมีคลาส 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() ในการอ้างอิงไปยังตำแหน่งของข้อมูลนั้น
นอกจากนี้ คุณยังลบได้โดยการระบุ Variant ที่เป็น null เป็นค่าสำหรับการดำเนินการเขียนอื่นๆ เช่น 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 จะซิงค์ข้อมูลนั้นกับเซิร์ฟเวอร์ฐานข้อมูลระยะไกลและกับไคลเอ็นต์อื่นๆ ตามความสามารถที่ดีที่สุด
ด้วยเหตุนี้ การเขียนข้อมูลทั้งหมดลงในฐานข้อมูลจึงทริกเกอร์เหตุการณ์ภายในทันที ก่อนที่จะมีการเขียนข้อมูลลงในเซิร์ฟเวอร์ ซึ่งหมายความว่าแอปของคุณจะยังคงตอบสนองได้ไม่ว่าเครือข่ายจะมีความหน่วงหรือการเชื่อมต่อเป็นอย่างไร
เมื่อมีการเชื่อมต่ออีกครั้ง แอปของคุณจะได้รับชุดเหตุการณ์ที่เหมาะสมเพื่อให้ไคลเอ็นต์ซิงค์กับสถานะเซิร์ฟเวอร์ปัจจุบันโดยไม่ต้องเขียนโค้ดที่กำหนดเอง